はじめてのCORS設定 @FastAPI
CORSに出会ったきっかけ
React と FastAPI を組み合わせて web アプリを作成する過程で、開発環境(例えば、自宅内の別端末や実機)からアクセスした際、予期せぬ通信エラーに悩まされる状況に直面しました。
原因は、ブラウザに備わる「同一オリジンポリシー」による通信制限でした。
この問題を解決する鍵となったのが、CORS(Cross-Origin Resource Sharing)という仕組みで、CORS を正しく設定することで、意図したオリジン間での通信が許可され、開発中の不具合を解消できることが分かりました。
CORSって何?
CORS(Cross-Origin Resource Sharing)は、ブラウザに実装されているセキュリティ機能のひとつで、「オリジン」(プロトコル、ドメイン、ポートの組み合わせ)が異なるリソース間でのアクセスを制限・許可する仕組み
CORSがなぜ必要か
そもそもwebブラウザは「同一オリジンポリシー」というルールに従っている。これは、
- オリジン:例)http://example.com:3000 と http://example.com:8000 はプロトコル、ドメイン、ポートが全て一致しないため、別のオリジンとみなされる
- 目的:悪意のあるスクリプトが他のサイトの情報に勝手にアクセスするのを防ぐ
要するに、フロントエンジンがあるオリジンが例えばhttp://localhost:3000 や http://192.168.1.23:3000で動作していても、バックエンドのAPIが別のオリジン(ここではfastapiを例にhttp://localhost:8000とする)で動いていると、ブラウザはこれを「クロスオリジンのリクエスト」とみなす。
CORSの役割
CORSは、この制限を緩和するために、サーバー側が特定のオリジンからのリクエストを「明示的に許可」するための仕組み。
- サーバーの役割:レスポンスヘッダーに Access-Control-Allow-Origin などのCORS関連ヘッダーを付与することで、特定のオリジンからのアクセスを許可できます。
- ブラウザの役割:このヘッダーの内容を確認し、リクエストが許可されているか判断します。
FastAPIでのCORS設定方法
- 必要なモジュールをインポート
from fastapi.middleware.cors import CORSMiddleware
- 許可するオリジンのリストを定義
origins = [
"http://192.168.1.10:3000", # 例えばローカル開発
"http://localhost:3000", # 例えばローカル開発
# 他の必要なオリジンもここに追加
]
- CORSMiddlewareの追加
app.add_middleware(
CORSMiddleware,
allow_origins=origins, # 許可するオリジンのリスト
allow_credentials=True, # Cookie等の資格情報も許可する場合に設定
allow_methods=["GET", "POST", "PUT", "DELETE"], # 許可するHTTPメソッド("GET", "POST" など); "*" は全てを許可
allow_headers=["*"], # 許可するHTTPヘッダー; "*" は全てを許可
)
この設定により、指定したオリジンからのリクエストはCORSエラーなくバックエンドにアクセスできるようになる。
実際の商用環境ではどうするんだっけ
実際の商用環境(AWSのようなクラウド環境)では、ネットワークレベルでのアクセス制御(例えばSecurity Group、NACL、WAF)を用いて、どのIPアドレスやネットワークからのアクセスを許可するかを厳密に管理することが一般的。
ただ、今回扱ったCORS(Cross-Origin Resource Sharing) は、ネットワークレベルのアクセス制御とは別の役割を持っている。以下、その違いと補完関係について説明。
ネットワークレベルのアクセス制御
- NACL(Network ACL)やSecurity Group
- 役割: VPC内のサブネットやインスタンスに対して、どのIPアドレスやポートからの通信を許可または拒否するかを制御。
- 用途: 外部からの不正アクセスや不要な通信を防ぐため、許可されたネットワークやIPアドレスからのみアクセスを許可するなど、インフラの境界で安全性を確保。
CORS(Cross-Origin Resource Sharing)
- 役割: ブラウザ側のセキュリティ機構として、JavaScriptなどのクライアントサイドのコードが、異なるオリジン(プロトコル・ドメイン・ポートの組み合わせ)のリソースにアクセスする際に、サーバーが明示的に許可しているかどうかを判断する仕組み。
- 用途: たとえば、バックエンドAPIが http://api.example.com で提供されていて、フロントエンドが http://www.example.com で動作している場合、ブラウザはこれを「クロスオリジン」のリクエストとみなされる。このとき、FastAPI側で適切にCORS設定を行い、Access-Control-Allow-Origin ヘッダーを返すことで、ブラウザがリクエストを許可するかどうかを決定する。
関係と補完性
異なるレイヤーでの制御:
- ネットワークレベル(NACL、Security Group)はインフラの「境界」でのアクセスを制御し、特定のIPレンジやポートからのみ通信を許可する。
- 一方、CORSはブラウザ内で実行されるJavaScriptのリクエストに対して、どのオリジンからのアクセスを許可するかを指定する、アプリケーションレベルのセキュリティです。
補完的な役割:
たとえば、社内や特定のパートナー向けのシステムの場合、NACLやSecurity Groupで通信元を限定することができれば、CORS設定を緩めてもリスクは低くなる。
しかし、一般公開のAPIやWebサービスの場合、どのIPからアクセスされるかわからないため、ネットワークレベルだけで制限するのは難しい。
このような場合、CORSはブラウザが不正なクロスオリジンリクエストをブロックするための重要な仕組みとなる。
結局、社内利用だったらネットワーク制御だけでいい?
その認識で合っています。社内システムで、さらにJavaで動作しているフロントエンドからのみAPIが呼ばれる場合、Bearerトークンとかで認証して、正当なクライアントだとわかれば良さそう。
つまり、商用環境で社内向けにAPIを提供する場合は、
- ネットワークアクセス制御(ファイアウォール、Security Group、NACL等)で外部からのアクセス自体を制限し、
- Bearer認証などのアプリケーションレベルの認証・認可で、正当なクライアントのみがAPIを利用できるようにする
でいいのではないでしょうか。
Discussion