コース目次 / 第2章

CORS の基本 — サーバが開ける窓口

APIがAccess-Control-Allow-Originで正規のフロントを名指しで許可し、fetchが通る様子を見ます。単純リクエストとプリフライト(OPTIONS)の違いを押さえます。

第2章 / 全5章目安 約10分この章のゴール: CORSがサーバ側のヘッダでSOPを明示的に緩める仕組みだと理解する

前章で、正規のフロント(:4001)ですらブロックされました。読ませたいなら、API が明示的に許可を出す必要があります。その許可が CORS(Cross-Origin Resource Sharing) です。

法と倫理ゲート

手を動かす前に

このコースは攻撃の技術に触れます。実習してよい相手は、次の3つだけです。

  • (a) 自分で作った練習用のやられ環境(このサイトが配る自作ターゲット)
  • (b) あなた自身の許可された環境(自分の Thousand SKY・secret-notes-ctf など)
  • (c) CTF 競技の中(規約の範囲で)

実在する第三者のシステムを許可なく触ることは、不正アクセス禁止法に触れます。 このゲートは、その線引きを毎回の入口で確かめるためのものです。詳しくは「セキュリティの法と倫理」へ。

窓口を、開ける

API(api.js)に、1行足します。「http://127.0.0.1:4001 には読ませてよい」と名指しで許可するヘッダです。

const server = http.createServer((req, res) => {
  res.setHeader('Content-Type', 'application/json; charset=utf-8');
  // CORS: このオリジンには、レスポンスを読ませてよい、と明示的に許可する。
  res.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:4001');
  res.end(JSON.stringify({ secret: '架空太郎の口座残高: 1,234,567 円' }));
});

API を再起動して(Ctrl+C → node api.js)、ブラウザで http://127.0.0.1:4001 を再読み込みします。

今度は 「読めた: {“secret”:“架空太郎の口座残高: 1,234,567 円”}」と表示されます。Access-Control-Allow-Origin(ACAO)で名指しされた :4001 は、ブラウザに「このオリジンには読ませてよい」と伝わり、JS が中身を読めるようになりました。
確かめ方も押さえておきます——curl -i -H “Origin: http://127.0.0.1:4001” http://127.0.0.1:4000 とすると、レスポンスヘッダに Access-Control-Allow-Origin: http://127.0.0.1:4001 が乗っているのが見えます。判断材料はサーバが出し、実際に読ませるか止めるかはブラウザが決める——この分担が CORS の核です。

単純リクエストと、プリフライト

CORS には2つのモードがあります。診断でよく効くので、違いを押さえます。

単純リクエスト(simple request) — GET や、素朴な POST のように「昔からフォームでも送れた」種類のリクエスト。ブラウザはいきなり本番のリクエストを送り、返ってきた ACAO を見て「読ませてよいか」を事後に判断します。
プリフライト(preflight) — PUT/DELETE や、独自ヘッダ・JSON の Content-Type を伴うリクエストなど、「副作用がありそう」な種類。ブラウザは本番の前に、OPTIONS という下見のリクエストを自動で送り、「このメソッド・このヘッダで、そちらへ送ってよいか?」をサーバに問い合わせます。サーバが Access-Control-Allow-Methods などで許可を返して初めて、本番を送ります。

プリフライトに答えるには、API が OPTIONS を処理する必要があります。

// プリフライト(OPTIONS)への応答。許可するメソッド・ヘッダを返す。
if (req.method === 'OPTIONS') {
  res.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:4001');
  res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
  res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
  res.writeHead(204);
  res.end();
  return;
}

診断では、この OPTIONS のやり取りが宝の山です。プリフライトの応答を見れば、その API が「どのオリジン・どのメソッド・どのヘッダ・資格情報を伴うか」を、どう許可しているかが分かる。次章で突く誤設定も、多くはこの応答ヘッダに現れます。

CORS は「緩める」だけ

大事な位置づけを、もう一度。

CORS は、SOP を破る攻撃手段ではありません。SOP という既定の壁に、サーバが意図的に窓口を開ける仕組みです。正しく使えば、正規のフロントにだけ窓を開け、それ以外は壁のまま。
問題は——窓を、開けすぎたときに起きます。「誰にでも読ませる」「資格情報付きで誰にでも」といった緩めすぎが、壁を無意味にする。それが次章です。

持ち帰る一言

CORS は、サーバが名指しで開ける窓口。 Access-Control-Allow-Origin で許可したオリジンだけが、別オリジンのレスポンスを読める。判断材料はサーバが出し、ブラウザが強制する。プリフライト(OPTIONS)は副作用のあるリクエストの下見。では、この窓口を「開けすぎる」と何が起きるか。次章で穴を見ます。

こうなっていればOK

卒業まであと2章です。

この章はまだ完了していません。