1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【概要編】CORS ~AWS100本ノック~ 14/100

Posted at

はじめに

みなさん、CORSって聞いたことありますか?
私自身、聞いたことはあるし、S3の設定をしたこともありますがいまいちわかってないままでした... :cry:
この記事では、CORSについて説明していきたいと思います。
※サンプルを交えた説明は別記事で書こうと思っています

CORSについて

そもそも何の略?

Cross-Origin Resource Sharingの略で、日本語だとオリジン間リソース共有になります。
別のオリジン間でリソースを共有する、って正直よくわからない...そもそもオリジンって何だろう... :thinking:

オリジンとは?

オリジンは、以下の3つから構成されます。

  • スキーム(プロトコル)
  • ポート番号
  • ホスト(ドメイン)

さぁ、突然ですが問題です。
以下の選択肢のうち、http://example.com/dir/index.htmlと同じオリジンになるのはどれでしょうか?

  1. http://example.com/dir2/new.html
  2. http://example.com/dir/inner/other.html
  3. https://example.com/page.html
  4. http://example.com:81/dir/page.html
  5. http://test.example.com/dir/page.html
答えはこちら

1と2が正解です:smiley:

  1. http://example.com/dir2/new.html👈ドメイン以下のパスが違いますが、同一オリジンです
  2. http://example.com/dir/inner/other.html👈ドメイン以下のパスが違いますが、同一オリジンです
  3. https://example.com/page.html👈プロトコルが違うので、別オリジンです
  4. http://example.com:81/dir/page.html👈ポート番号が違うので、別オリジンです
  5. http://test.example.com/dir/page.html👈ホストが違うので、別オリジンです

ドメインとオリジンは別物なので、ご注意を!!!

Same-OriginPolicy(同一オリジンポリシー)

CORSの説明をする上で必ず出てくるのが、同一オリジンポリシーです。
これは、Webブラウザに搭載されている重要なセキュリティメカニズムで、異なるオリジンのリソースにアクセスすることを制限します。

CORS-ページ6.drawio (1).png

同一オリジンポリシーの目的

そもそも異なるオリジンへのアクセスをなぜ制限する必要があるのでしょうか?
それは、CSRFなどの脆弱性から個人情報などの大切なデータを守るためです。

CSRF(クロスサイトリクエストフォージェリ)とは?

CSRFは、Webアプリケーションの脆弱性を利用してユーザが意図しない形で処理を実行するサイバー攻撃です。

流れとしては以下のようになります。

1-1. ユーザがECサイトにログインし、リクエストをします
1-2. ログイン状態などをチェックし、正しいリクエストなので問題なく受け付けます
2. 攻撃者が、偽のECサイトへ巧みに誘導するようなリンク付きのメールなどを送信します
3-1. それを受け取ったユーザは気づかずに、リンクをクリックします
3-2. 不正なリクエストが飛びますが、(もしサーバに脆弱性がある場合は)1-1でログインしているため受け付けてしまいます

これは非常に危険ですね:scream:
ただ、同一オリジンポリシーがあれば、リクエストをブロックしてくれて安心ですね

...と思いきや、そうでもありません。

CORS-CSRF.drawio (5).png

先ほどの図をもう一度見てみましょう!

異なるオリジンへのリクエストに関するレスポンスをブラウザがブロックしてくれるので、
読み取り操作(不正に個人情報を取得する、など)については効果がありそうです

しかし、図を見てわかる通りリクエストを送ることは出来てしまうので、サーバがリクエストを処理してしまうと不正にデータ登録などを行えてしまいます。
つまり、同一オリジンポリシーではセキュリティ的に不十分で、必ずサーバ側の対応が必要になるということです。

同一オリジンポリシーの制限の対象になるもの/ならないもの

全てのリクエストが同一オリジンポリシーの対象になるかというと、そうではありません。
以下のように制限の対象になるものと、対象にならないものがあります。

制限の対象になるもの

  • JavaScriptでの非同期通信(XMLHttpRequestやFetch APIを使用した通信)
  • Canvas
  • Web Storage

制限の対象にならないもの

  • <script>で読み込まれたJavaScript
  • <link>で読み込まれたCSS
  • <img>で表示された画像
  • <video>、<audio>で再生されたメディアファイル
  • <object>、<embed>で埋め込まれた外部リソース
  • @font-faceが適用されたフォント ※異なるオリジンは不可のブラウザもあり
  • <iframe>でのコンテンツの読み込み ※レスポンスヘッダーX-Frame-Options(非推奨)にSAMEORIGINが設定されている場合、異なるオリジンは不可

CORSがなぜ必要か

同一オリジンポリシーで、異なるオリジンへのアクセスを制限することはわかりました。
しかし、実際のWebアプリケーションで別オリジンにリクエストしないことの方が少ないと思います。

  • 何かしら情報を取得したり、データ連携のために外部APIを利用する
  • サブシステムにアクセスする
  • S3にフロントエンドからファイルをアップロードする

こういったケースに対応するための異なるオリジンにあるリソースへのアクセス権をブラウザーに与えるための仕組みがCORSになります。

CORSの仕組み

ここからCORSの仕組みについて説明していきます。
異なるオリジンへリクエストを送る場合、リクエストはSimpleRequest(単純リクエスト)PrefrightRequestの2種類に分かれます。

それぞれの流れを図で表すと以下のようになります。
※図で出てくるヘッダーについては、後程説明します

SimpleRequest

SimpleRequestは同一オリジンでのリクエストと同様に、サーバにリクエストを送ってレスポンスを受け取る形です。
もし、サーバー側で許可されていないOriginやメソッドの場合は、以下のようなCORSエラーになります。

Access to fetch at 'https://static-hosting-cors-server.s3.ap-northeast-1.amazonaws.com/preflight-correct.png' from origin 'http://static-hosting-cors-client.s3-website-ap-northeast-1.amazonaws.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

SimpleRequestでCORSエラーの場合は、レスポンスのHTTPステータスとしては200になりました。
ブラウザの仕様なのか、S3側の問題ないのかここのはよくわからなかったので知ってる方はコメントで教えてください :bow:

CORS-シンプルリクエスト.drawio (1).png

PrefrightRequestと違い、最初から本リクエストを送ってしまうため、CORS設定などに不備があると予期せぬ処理(データ更新)が実行されてしまいます。
ブラウザでレスポンスを見ることが出来ないからOKではない、ということです。

CORS設定は正しく行い、全てのOriginを許可する必要がある場合などは、必ず認証チェックなどの対策をするようにしましょう!

PrefrightRequest

PrefrightRequestが発生するリクエストの場合はまず、OPTIONSで同じ本リクエスト同じURLにアクセスします。
サーバー側で確認し問題がなければ200が返り、CORSエラーの場合は403が返ります。

PrefrightRequestのレスポンスで、ブラウザが本リクエストを送っても良いか判断し、本リクエストを送ります。この判断には後述のAccess-Controlヘッダーが使われます。

CORS-プリフライトリクエスト.drawio (1).png

SimpleRequestとPrefrightRequest

さて、CORSのリクエストには2種類あるとわかりましたが、
どういう場合にSimpleRequestで、どういう場合にPrefrightRequestとなるのでしょうか?

となります。

1.HTTPメソッドが下記のいずれかである

  • GET
  • HEAD
  • POST

2.ヘッダーに含まれるものが以下のみである

  • CORSセーフリクエストヘッダー(PrefrightRequestを必要としないヘッダー)
    • Accept
    • Accept-Language
    • Content-Language
    • Content-Type(以下のいずれか以外の場合は、SimpleRequestではない)
      • application/x-www-form-urlencoded
      • multipart/form-data
      • text/plain
    • Range
  • ユーザーエージェントによって自動的に設定されたヘッダー(上書きが出来ないヘッダー)

3.リクエストに使用されるどの XMLHttpRequestUpload にもイベントリスナーが登録されていないこと

4.リクエストにReadableStreamオブジェクトが使用されていない

ヘッダーについて

次にCORSで利用されるリクエストヘッダー/レスポンスヘッダーについて説明します。

リクエストヘッダー

Origin

リクエスト元のオリジンです。
クライアントが意識しなくても自動的に設定されます。また、この値は偽造することも出来ません。
ex Origin:https://example.com

Access-Control-Request-Headers

リクエストするカスタムヘッダーです。
SimpleRequestになるための条件で記載したヘッダー以外をリクエストする際に、自動的にこのヘッダーにも値が設定されます。
なので、SimpleRequesではこのヘッダーは送信されません。
ex Access-Control-Request-Headers: Content-Type Authorization Hoge

Access-Control-Request-Method:

リクエストするHTTPメソッドが設定されます。
なので、SimpleRequesではこのヘッダーは送信されます。
ex Access-Control-Request-Method: GET, PUT

レスポンスヘッダー

Access-Control-Allow-Origin

アクセスを許可するオリジンを1つ以上指定します。許可リストにないオリジンからリクエストするとCORSエラーになります。
*と指定して全てを許可することも出来ますが、基本的には必要なオリジンにだけ絞ったほうが良いです。
資格情報のあるCORSリクエストの場合は、*を指定するとエラーになります。
ex Access-Control-Allow-Origin: https://example.com

Access-Control-Allow-Headers

リクエストを許可するカスタムヘッダーを指定します。許可リストにないカスタムヘッダーをリクエストするとCORSエラーになります。
資格情報のないリクエストの場合のみ*と指定して全てを許可することも出来ますが、基本的には必要なヘッダーにだけ絞ったほうが良いです。
資格情報のあるCORSリクエストの場合は、*を指定するとエラーになります。
カスタムヘッダーの許可が不要な場合は、指定しなくても問題ありません
ex Access-Control-Allow-Headers: Content-Type Authorization Hoge

Access-Control-Allow-Methods

リクエストを許可するメソッドを1つ以上指定します。許可リストにないメソッドでリクエストするとCORSエラーになります。
ex Access-Control-Allow-Methods: GET, PUT

Access-Control-Expose-Headers

レスポンスとして公開するヘッダーを記載します。
ここで指定したヘッダー以外は、以下のCORS セーフリストレスポンスヘッダーを除いてクライアント(JavaScript)から扱うことが出来ません。
ex Access-Control-Expose-Headers: Content-Type Authorization Hoge

  • Cache-Control
  • Content-Language
  • Content-Length
  • Content-Type
  • Expires
  • Last-Modified
  • Pragma

Access-Control-Allow-Credentials

異なるオリジン間リクエストに、資格情報(Cookie、認証ヘッダー、TLSクライアント証明書)を含めるか指定します。
含める場合は、trueを設定します。
資格情報のあるCORSリクエストの場合にtrueを設定しないとエラーになります。
ex Access-Control-Allow-Credentials: true

Access-Control-Max-Age

PrefrightRequestの結果をキャッシュする秒数を指定します。
この時間内であれば、ブラウザはPrefrightRequestを送りません。
指定できる秒数はブラウザによって違うようです。
ex Access-Control-Max-Age: 300

おわりに

今回はCORSについて概要や仕組みを説明しました。
少し記事が長くなってしまったので、実際に試してみる系は別記事で書きたいと思います :smile:

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?