TT Lab
はじめる
学ぶ 学習パス コース

FastAPI — 型がそのまま契約だ

CORSは認証ではない

TT Labで続きを見る

目標

オリジン・メソッド・ヘッダーの許可マトリクスを、実際のプリフライトで検証します。

なぜ重要なのか

フロントエンドでレスポンスを読めなかったので、すべてのオリジンにアスタリスクを許可しました。クッキーを送るリクエストでは、ポリシーがさらに複雑になり、開発者は、CORSを有効にするだけで外部からのリクエストが遮断されると誤解しました。このラボでは、ブラウザーの読み取りポリシーと、サーバーの認証を分離します。認証の機能を代わりに作ることはしません。

ステップ

  1. /root/work/fa-cors-policy-lab/service.pyで、origin(value)は、httpまたはhttpsのURLで、hostがあり、path・query・fragment・ユーザー情報がなければ、入力の文字列を返します。それ以外は、ValueErrorです。末尾の/もpathなので、拒否します。

最初に一度だけ準備してください。既存のファイルは上書きしません。

mkdir -p /root/work/fa-cors-policy-lab
test -e /root/work/fa-cors-policy-lab/service.py || cp /opt/fixtures/ten_labs/fa-cors-policy-lab/service.py /root/work/fa-cors-policy-lab/service.py
cd /root/work/fa-cors-policy-lab
  1. /root/work/fa-cors-policy-lab/service.pyで、origins(values)は、各項目をoriginで検証したあと、最初に出てきた順序で重複を除去した新しいリストです。

  2. /root/work/fa-cors-policy-lab/service.pyで、methods(values)は、GET・POST・PUT・DELETE・OPTIONSだけを許可して、大文字に変換し、重複を除去します。空のリストやそれ以外の値は、ValueErrorです。

  3. /root/work/fa-cors-policy-lab/service.pyで、policy(allowed, credentials)は、credentialsがboolであることを確認します。allowedに'*'があればValueErrorで、{allow_origins:origins(allowed), allow_credentials:credentials}を返します。

  4. /root/work/fa-cors-policy-lab/service.pyで、create_app(allowed, credentials=True)は、policyを検証して、CORSMiddlewareを設定したアプリです。GET/POSTだけを許可し、Content-Type・X-Request-IDのリクエストヘッダーを許可し、X-Traceのレスポンスヘッダーをexposeします。GET /dataは、{ok:True}、X-Trace='trace-1'を返します。

  5. /root/work/fa-cors-policy-lab/service.pyで、preflight_headers(source, method, requested='X-Request-ID')は、Origin、Access-Control-Request-Method、Access-Control-Request-Headersの3つのキーを持つ辞書です。methodは大文字です。

  6. /root/work/fa-cors-policy-lab/service.pyで、preflight_status(app, source, method, requested='X-Request-ID')は、TestClientで/dataにOPTIONSのリクエストを送って、HTTPのステータスを返します。別のオリジン・DELETE・X-Secretヘッダーは、400でなければなりません。

  7. /root/work/fa-cors-policy-lab/service.pyで、cors_observation(app, source)は、GET /dataを送って、(ステータス、Access-Control-Allow-Originの値またはNone、JSONの本文)を返します。許可されていないオリジンでも、200の本文は実行されますが、許可するオリジンのヘッダーは付いていてはいけません。

参考

オリジンの形式を検証する

/root/work/fa-cors-policy-lab/service.pyで、origin(value)は、httpまたはhttpsのURLで、hostがあり、path・query・fragment・ユーザー情報がなければ、入力の文字列を返します。それ以外は、ValueErrorです。末尾の/もpathなので、拒否します。

最初に一度だけ準備してください。既存のファイルは上書きしません。

mkdir -p /root/work/fa-cors-policy-lab
test -e /root/work/fa-cors-policy-lab/service.py || cp /opt/fixtures/ten_labs/fa-cors-policy-lab/service.py /root/work/fa-cors-policy-lab/service.py
cd /root/work/fa-cors-policy-lab

URL全体をオリジンとして許可すると、パスやユーザー情報を混同することがあります。

保存したら、bash /opt/lab/checks/fa-cors-policy-lab/01-contract.shで確認してください。

重複したオリジンを除去する

/root/work/fa-cors-policy-lab/service.pyで、origins(values)は、各項目をoriginで検証したあと、最初に出てきた順序で重複を除去した新しいリストです。

許可リストは、文字列の部分一致ではなく、正確なオリジンのリストです。

保存したら、bash /opt/lab/checks/fa-cors-policy-lab/02-contract.shで確認してください。

メソッドを許可リストで制限する

/root/work/fa-cors-policy-lab/service.pyで、methods(values)は、GET・POST・PUT・DELETE・OPTIONSだけを許可して、大文字に変換し、重複を除去します。空のリストやそれ以外の値は、ValueErrorです。

許可していないPATCHや任意のメソッドを、黙って追加しません。

保存したら、bash /opt/lab/checks/fa-cors-policy-lab/03-contract.shで確認してください。

認証情報とアスタリスクを一緒に許可しない

/root/work/fa-cors-policy-lab/service.pyで、policy(allowed, credentials)は、credentialsがboolであることを確認します。allowedに'*'があればValueErrorで、{allow_origins:origins(allowed), allow_credentials:credentials}を返します。

このラボの明示的なポリシーは、認証情報の有無にかかわらず、アスタリスクを受け付けません。

保存したら、bash /opt/lab/checks/fa-cors-policy-lab/04-contract.shで確認してください。

実際のCORSミドルウェアを付ける

/root/work/fa-cors-policy-lab/service.pyで、create_app(allowed, credentials=True)は、policyを検証して、CORSMiddlewareを設定したアプリです。GET/POSTだけを許可し、Content-Type・X-Request-IDのリクエストヘッダーを許可し、X-Traceのレスポンスヘッダーをexposeします。GET /dataは、{ok:True}、X-Trace='trace-1'を返します。

preflightと実際のレスポンスに、ヘッダーを手で別々に付けると、2つのポリシーが簡単にずれます。

保存したら、bash /opt/lab/checks/fa-cors-policy-lab/05-contract.shで確認してください。

プリフライトのリクエストを作る

/root/work/fa-cors-policy-lab/service.pyで、preflight_headers(source, method, requested='X-Request-ID')は、Origin、Access-Control-Request-Method、Access-Control-Request-Headersの3つのキーを持つ辞書です。methodは大文字です。

実際のリクエストのメソッドはOPTIONSで、検査したいメソッドは、別のヘッダーにあります。

保存したら、bash /opt/lab/checks/fa-cors-policy-lab/06-contract.shで確認してください。

拒否のマトリクスを計算する

/root/work/fa-cors-policy-lab/service.pyで、preflight_status(app, source, method, requested='X-Request-ID')は、TestClientで/dataにOPTIONSのリクエストを送って、HTTPのステータスを返します。別のオリジン・DELETE・X-Secretヘッダーは、400でなければなりません。

拒否の理由の3種類を、1つのリクエストに混ぜないようにしてはじめて、抜けているポリシーを見つけられます。

保存したら、bash /opt/lab/checks/fa-cors-policy-lab/07-contract.shで確認してください。

CORSと認証の違いを観察する

/root/work/fa-cors-policy-lab/service.pyで、cors_observation(app, source)は、GET /dataを送って、(ステータス、Access-Control-Allow-Originの値またはNone、JSONの本文)を返します。許可されていないオリジンでも、200の本文は実行されますが、許可するオリジンのヘッダーは付いていてはいけません。

curlやサーバー間のリクエストは、ブラウザーのCORSの読み取り制限に従いません。

保存したら、bash /opt/lab/checks/fa-cors-policy-lab/08-contract.shで確認してください。