セキュリティ

CORSについて簡潔に整理

CORS(Cross-Origin Resource Sharing)って知ってはいるけど、やっぱりつまづいてイラつく人っているよね

僕もそう、過去何回かCORSについての記事は書いたことがある

まあ似たような内容が若干被っているのはご愛嬌で、今回はMDNを読んだ内容(https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)をベースに再整理してみました

CORSとは

CORSとは同一オリジン以外へのリクエストをするときに、「お前のサービスのリソースではないと思うからリクエスト先に許可取っとくね」というブラウザのセキュリティ的な理由によるお節介です

許可が通らないとブラウザはリクエストの送信自体を行いません

同一オリジンブラウザが判断するのはMDNの同一オリジンポリシー(https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy)によると

  • プロトコル(http, https)が同じ
  • ポート番号が同じ
  • ホスト名が同じ

ということですなので

http://domain-a.comからリクエストを送信する場合、以下のリソースへはCORSとしてブラウザに判断されます

  • https://domain-a.com :プロトコルが違う(http ≠ https)
  • http://domain-a.com:8080:ポート番号が違う(80 ≠ 8080)
  • http://domain-b.com:ホスト名が違う(domain-a.com ≠ domain-b.com)
  • http://hoge.domain-a.com:サブドメインをつけてもダメ(domain-a.com ≠ hoge.domain-a.com)

CORSを通すには

いやいや、今時のWeb開発ではオリジンが違うサイトへのリクエストなんてしょっちゅう起きるでしょw

今時はモノリスじゃなくって、サーバーとフロントはドメインを分けてるもんでしょww

セキュリティ的な理由があるとはいえ同一オリジンポリシーは完全一致以外は認めないというのはなかなか辛い。個人的にはサブドメインくらいは許して欲しかった

といった現実があるので、CORSをちゃんと通すための方法を理解しておく必要性があるのです。

 

CORSにおいてブラウザはサーバへのリクエストから許可をもらうために

プリフライトリクエストというHTTPヘッダーのみのリクエストを送ります。

プリフライトリクエストに対してサーバから正常なレスポンスで返してくれたら、ブラウザはそのドメインへのリクエストを試みようとします。

プリフライトリクエストについて

では実際プリフライトリクエストについて説明しますと

フロントサイドとサーバサイドで取り決めたヘッダーをOPTIONSメソッドで送ることになります。

 

フロントサイドは自分が何者で何を許可してほしいのかを送る

  • Origin:どこからリクエストを送信しているのか(例:https://example.com)
  • Access-Control-Request-Method:どのリクエストメソッドを許可してほしいのか(GET, POST, PUT, DELETEなど)
  • Access-Control-Request-Headers:どのリクエストヘッダーを許可してほしいのか

 

サーバサイドは誰に対して何を許可するのかを送る

  • Access-Control-Allow-Origin:どこからのリクエストを許可するのか
  • Access-Control-Allow-Methods:どのリクエストメソッドを許可するのか
  • Access-Control-Allow-Headers:どのリクエストヘッダーを許可するのか
  • Access-Control-Max-Age:どのくらいの時間許可するのか

引用(https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)

上のプリフライトリクエストの例だと

  • Origin:https://foo.example
  • Access-Control-Request-Method:POST
  • Access-Control-Request-Headers:X-PINGOTHER, Content-Type

がフロントサイドが送るリクエストで

  • Access-Control-Allow-Origin:https://foo.example
  • Access-Control-Allow-Methods:POST, GET, OPTIONS
  • Access-Control-Allow-Headers:X-PINGOTHER, Content-Type
  • Access-Control-Max-Age:86400

とサーバサイドからレスポンスが204コードで返ってきていますので、プリフライトリクエストが通ったことになります。

認証情報込みのCORSの場合

CORSはCookieのような認証情報込みのリクエストになると、また一段と話がややこしくなります。

認証されたユーザ以外からのリクエストを極力弾きたいのでプリフライトリクエストの中身はもっとしっかりとした物にしたいといけないからです。

ではフロントサイドとサーバサイドでそれぞれどのようにすればいいのかというと

 

フロントサイド

  • HTTPリクエストを送信するときにクレデンシャル(認証情報)を許可する

XMLHttpRequestを例に挙げると

引用(https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)

 

サーバサイド

  • Access-Control-Allow-Orignにワイルドカード*を使わずに厳密なオリジンを指定すること
  • Access-Control-Allow-Headersに*を使わずに明確なヘッダーを指定すること
  • Access-Control-Allow-Methodsに*を使わずに明確なメソッドを指定すること

となります。

まとめ

要するにCORSとは

  • 同一ドメイン(プロトコル、ポート、ホスト)ではないリソースへのアクセスを制限するブラウザのセキュリティ
  • プリフライトリクエストを正しく通せばOK
  • 認証情報が絡んでいる場合だとプリフライトリクエストはしっかりとしないといけない

です。

今回も割と手抜き気味な内容になっちゃいましたが最後まで読んでいただいてありがとうございました。

-セキュリティ

© 2023 Shiro's secret base