CORSについてちゃんと理解する〜フロントエンド開発のためのセキュリティ入門を読む〜
フロントエンド開発のためのセキュリティ入門の第4章のCORS周りについてのおぼえがき
ブラウザはデフォルトで同一オリジンポリシーを有効にしているが、これが開発の妨げになることもある。
※同一オリジンポリシー:一定の条件において、クロスオリジンのリソースへのアクセスを制限する仕組み。
同一オリジンポリシーがあると...
- 自社で開発しているWebサイトが複数あり、それらが異なるオリジンの場合にアクセス制限されてしまう
- オリジンが異なるCDNから配信されているリソース取得が失敗する
→信用性があるサイトやCDNなどのクロスオリジンへアクセスできるようにCORSという仕組みを使う
CORS:Cross-Origin Resourse Sharing = オリジン間リソース共有
レスポンスに付与されている一連のHTTPヘッダによって、サーバから許可されたリソースへはアクセス可能になる
→このHTTPヘッダのことをCORSヘッダと呼ぶ
単純リクエスト
CORS-safelistedとみなされたHTTPメソッドやHTTPヘッダのみが送信されるリクエスト
- CORS-safelisted method:GET、HEAD、POST
- CORS-safelisted method request-header:Accept、Accept-Language、Content-Language、Content-Type
アクセスを許可するオリジンをブラウザを伝えるためには、以下レスポンスヘッダをサーバから送信する。※複数指定不可。
Access-Control-Allow-Origin: https://site.example
プリフライトリクエスト
PUTやDELETEなどのHTTPメソッドは送信前にブラウザからサーバへ問題ないことを確認する。
その結果を元に、リクエストの内容が許可されているかどうかを確認し、許可されていればリクエストを送信する。
→このリクエストのことを、プリフライトリクエストと呼ぶ。
プリフライトリクエストがなかったら...
直接リクエストのAccess-Control-Allow-Originヘッダ:あくまでレスポンスのリソースへのアクセスをJavaScriptに許可するもの
→リクエスト自体は行われているため、サーバ内の処理を止めることはできない
↓つまり...
ユーザ自身のブラウザに保存されているログイン情報を使ってPUTやDELETEなどの操作の攻撃を受けた場合、
クロスオリジンであっても、サーバにリクエストが届いてしまい、処理は実行されてしまう = データ削除されてしまう
プリフライトリクエストの流れ
リクエスト:ブラウザ→サーバ
OPTIONSメソッドを使って、
リクエストを送信するオリジン、利用したいHTTPメソッド、付与したいHTTPヘッダを送信することで、それらがクロスオリジンから利用可能か確認できる。
- リクエストを送信するオリジン
Origin: https://site.exmaple
- 利用したいHTTPメソッド
Access-Control-Request-Method: DELETE
- 付与したいHTTPヘッダ(=送信するリクエストに含まれるHTTPヘッダ)
Access-Origin-Request-Headers: content-type
レスポンス:サーバ→ブラウザ
OPTIONによるプリフライトリクエストの結果以下のレスポンスが返る。
- アクセスを許可するオリジン
Access-Control-Allow-Origin: https://site.exmaple
- 利用できるHTTPメソッド
Access-Control-Allow-Methods: GET, PUT, DELETE, OPTIONS
- 利用できるHTTPヘッダ
Access-Control-Allow-Headers: Content-Type, Authorization ~~
- リクエストの結果をキャッシュする秒数
Access-Control-Max-Age: 3600
Access-Control-Max-Age
を使うことで、プリフライトリクエストの結果をブラウザにキャッシュすることができる
→ボトルネックを防げる
Cookieを含むリクエスト
ページ遷移やフォーム送信時には、ブラウザはCookieをサーバーへ送信する。
クロスオリジンの通信ではCookieは送信されない。
クロスオリジンのサーバへCookieを送信する場合は、以下のような明示が必要。
ブラウザ側
-
fetch関数:credentialsオプションを設定
https://developer.mozilla.org/ja/docs/Web/API/fetch#credentials -
XMLHttpRequestの場合:withCredentialsをtrueに設定
https://developer.mozilla.org/ja/docs/Web/API/XMLHttpRequest/withCredentials
サーバ側
Access-Control-Allow-Credentials
ヘッダをtrueに設定し送信する
※このとき、明示的にオリジンも設定(Access-Control-Allow-Origin: https://~~
)しておくことが必要
CORSのリクエストモード
フロント側でCORSを行うかどうかを設定することも可能(今まではサーバ側のCORSヘッダの話)
ほとんどのブラウザではデフォルトでCORSを行うように設定されている
これがデフォルト値
fetch(url, { mode: 'cors' });