CORSの基本概念
CORS(Cross-Origin Resource Sharing)は、ウェブブラウザが異なるオリジン間でリソースを共有する際に適用されるセキュリティ機能です。
ウェブブラウザは、同一オリジンポリシーに基づいて異なるオリジンからのリソースへのアクセスを制限します。このため、CORSを利用して特定のオリジンからのアクセスを許可する必要があります。
オリジンはプロトコル(http, httpsなど)、ドメイン(example.comなど)、ポート番号(80, 443など)から構成されています。
例えば、http://example.com
とhttps://sample.com
は異なるオリジンと見なされるため、CORSを設定してアクセスを許可しないといけません。
なぜ同一オリジンポリシーが必要?
1. ユーザーのデータを保護するため
そもそも、ウェブアプリケーションは、認証済みセッションやクッキーを利用してユーザーを識別し、機能を提供します。
もし異なるオリジン間でアクセスできるようになると以下のようなことが起こります。
・クッキーの不正使用
ユーザーがhttps://example.com
にログインしている間に、悪意のあるウェブサイトがユーザーのクッキーを利用して example.com の情報にアクセスできてしまう。
・セッションハイジャック
攻撃者がユーザーの認証済みセッションを乗っ取って、そのユーザーとして行動できる。
2. クロスサイトスクリプティング (XSS) 攻撃を防ぐため
同一オリジンポリシーがない場合、悪意のあるウェブサイトがユーザーのブラウザで任意のスクリプトを実行し、別のサイトのデータにアクセスできるようになります。これにより、以下のことが可能になります。
・機密情報の盗難
ユーザーの個人情報やクレジットカード情報が流出する。
・改ざんや偽情報の表示
ユーザーがアクセスしている正規サイトの表示内容を攻撃者が変更する。
3. APIやサーバーリソースの不正利用を防ぐため
同一オリジンポリシーがなければ、攻撃者が自由に他のオリジンの API を呼び出すことができます。これにより以下のような被害が生じます。
・リソースの消耗
他のオリジンのサーバーを意図的に攻撃する(リクエストを大量に送りつける)。
・機能の悪用
他のオリジンで提供されている有料機能や限定機能を不正に利用する。
CORSの限界は?
CORSだけでは完璧にセキュリティ対策ができているわけではありません。以下にCORSの限界をいくつか挙げます。
1. CORS はブラウザ依存の機能
CORS は主にブラウザでのセキュリティ制約を緩和するために設計されています。そのため、以下のような限界があります。
・非ブラウザ環境では無効
CORS はブラウザが実装している仕組みなので、ブラウザを介さないリクエスト(例: curl や Postman、サーバーサイドからのリクエスト)には適用されません。そのため、攻撃者が直接サーバーにリクエストを送信すれば、CORS 設定は無意味になります。
2. サーバー側での設定ミス
サーバー側でのCORS の設定が不適切だと、攻撃のリスクが高まります。
・Access-Control-Allow-Origin: * の濫用
オリジンを無制限 (*) に許可する設定は非常に危険です。これにより、あらゆるウェブサイトがリソースを利用できるようになるため、意図しない第三者がリソースにアクセスする可能性があります。
・信頼できないドメインの許可
特定のオリジン(例:https://malicious-site.com
)を許可する場合、攻撃者がそのオリジンを利用してリクエストを送ることが可能になります。
3. CORS だけでは XSS や CSRF を防げない
CORS はリソース共有の制御に特化しており、クロスサイトスクリプティングやクロスサイトリクエストフォージェリは防げません。
・クロスサイトスクリプティング (XSS)
攻撃者が正規のウェブサイトでスクリプトを実行することで、ユーザーの情報を盗む攻撃には無力です。
・クロスサイトリクエストフォージェリ (CSRF)
CORS 設定でリクエストが制限されていても、攻撃者がユーザーの認証済みセッションを悪用して不正なリクエストを送信することを防ぐことはできません。
例えば、Access-Control-Allow-Credentials: true
を設定している場合、攻撃者がクッキーやセッション情報を利用して、ユーザーとしての操作を装うことが可能になります。
4. クライアント側の CORS 設定は無力
CORS はサーバー側で設定されるべきものであり、クライアント側では設定できません。
そのため、フロントエンドが CORS を「設定」できるわけではなく、CORS 設定がない場合に対処する手段は限られています。
CORS を補完する対策
1. CSRF トークンの利用
CSRF は、攻撃者がユーザーのブラウザを利用して不正なリクエストを送信させる攻撃です。
CORS 設定では、リクエストの送信自体を防げない場合があるため、CSRF トークンで追加の防御を行います。
・CSRF トークンの仕組み
・サーバーがユーザーのセッションに関連付けられた一意のトークンを生成。
・フォームや AJAX リクエストにこのトークンを含める。
・サーバーはリクエスト内のトークンを検証し、正当性を確認。
2. 認証と認可の強化
・認証: ユーザーが誰であるかを確認。
・認可: ユーザーが何を許可されているかを確認。
CORS はリソース共有を制御しますが、認証や権限管理が適切でなければ、攻撃者が正規ユーザーとしてリソースを操作するリスクがあります。
3. XSS 対策
CORS は XSS 攻撃を防げないため、別の対策が必要です。
・入力のサニタイズ
ユーザー入力を検証し、不正なスクリプトを除去。
・コンテンツセキュリティポリシー (CSP)
スクリプトの実行元を制限。
・実装例 (HTML ヘッダー)
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' https://trusted.com">
CORSの仕組み
CORS のリクエストは主に以下の 2 種類に分類されます。
1.シンプルリクエスト
以下を満たすリクエストがシンプルリクエストに該当します。
・HTTP メソッド: GET, POST, または HEAD のいずれか。
・HTTP ヘッダー: 以下の標準的なヘッダーのみを使用。
・Accept
・Accept-Language
・Content-Language
・Content-Type(ただし、値が application/x-www-form-urlencoded、multipart/form-data、text/plain の場合のみ)
・その他: カスタムヘッダーや Authorization ヘッダーは使用しない。
・動作の流れ
1.ブラウザがリソースをリクエスト(例:https://api.example.com
にアクセス)。
2.サーバーが CORS ヘッダーをレスポンスに含める(例: Access-Control-Allow-Origin)。
3.ブラウザがヘッダーを検証し、条件に一致すればレスポンスをクライアントに渡す。
2. プリフライトリクエスト
以下の場合、プリフライトリクエストが必要です。
・HTTP メソッド: PUT, DELETE, PATCH などのメソッドを使用。
・カスタムヘッダー: 標準的なヘッダー以外を含む。
・クレデンシャル情報: withCredentials: true を指定した場合。
・動作の流れ
1.プリフライトリクエスト (OPTIONS メソッド) の送信
ブラウザは、実際のリクエストを送る前にサーバーに以下のようなプリフライトリクエストを送信します。
OPTIONS /resource HTTP/1.1
Host: api.example.com
Origin: https://example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-Custom-Header
2.サーバーからのレスポンス
サーバーがリクエストを許可する場合、以下のようなレスポンスを返します。
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-Custom-Header
Access-Control-Max-Age: 3600
・Access-Control-Allow-Origin: 許可するオリジンを指定。
・Access-Control-Allow-Methods: 許可する HTTP メソッドを指定。
・Access-Control-Allow-Headers: 許可するカスタムヘッダーを指定。
・Access-Control-Max-Age: プリフライトリクエストの結果をキャッシュする時間(秒)。
3.実際のリクエストの送信
ブラウザはプリフライトリクエストが成功すると、続けて実際のリクエストを送信します。
POST /resource HTTP/1.1
Host: api.example.com
Origin: https://example.com
X-Custom-Header: customValue
Content-Type: application/json
{ "key": "value" }
CORSの設定例
1. FastAPIのCORS設定
FastAPIでは、fastapi.middleware.cors.CORSMiddleware
を使って簡単にCORSを設定できます。
以下は、特定のオリジン(ドメイン)からのリクエストを許可するFastAPIアプリケーションの例です。
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
# 許可するオリジン(例: フロントエンドのURL)
origins = [
"http://localhost:3000", # React開発用サーバー
"https://example.com" # 本番環境のフロントエンドURL
]
# CORSミドルウェアの追加
app.add_middleware(
CORSMiddleware,
allow_origins=origins, # 許可するオリジン
allow_credentials=True, # 認証情報(クッキーやヘッダー)を許可
allow_methods=["GET", "POST"], # 許可するHTTPメソッド
allow_headers=["*"], # 許可するカスタムヘッダー
)
@app.get("/")
async def root():
return {"message": "Hello, World!"}
2. S3のCORS設定
S3バケットに保存されているリソースをCORS対応にするには、バケットポリシーで設定を行います。
AWS Management ConsoleでS3バケットを選択したら、「プロパティ」 > 「CORS設定」を開く。
CORSルールの記述に以下のようなCORSルールをJSON形式で記述します。
[
{
"AllowedHeaders": ["Authorization", "Content-Type"],
"AllowedMethods": ["GET", "POST", "PUT"],
"AllowedOrigins": ["https://example.com", "http://localhost:3000"],
"ExposeHeaders": ["ETag"],
"MaxAgeSeconds": 3000
}
]
・AllowedHeaders: クライアントが使用可能なヘッダーを指定します。
・AllowedMethods: 許可するHTTPメソッド(例: GET, PUT)。
・AllowedOrigins: 許可するオリジン(例: フロントエンドのURL)。
・ExposeHeaders: レスポンスでクライアントがアクセス可能なヘッダーを指定します。
・MaxAgeSeconds: プリフライトリクエストの結果をキャッシュする時間。