woshidan's blog

そんなことよりコードにダイブ。

OPTIONSメソッドが気になったのでCORSとプリフライトリクエストについて少し調べました

Amazon API GatewayでCORSを有効にするとOPTIONSメソッドが追加されるのが気になったので、CORSについて少し調べました。

CORSとは

オリジン間リソース共有Cross-Origin Resource Sharing (CORS) は、追加の HTTP ヘッダーを使用して、あるオリジン (ドメイン) で動作しているウェブアプリケーションに、異なるオリジンのサーバーにある選択されたリソースへのアクセスを許可することができる仕組みです。ウェブアプリケーションは、自分のオリジンとは異なるオリジン (ドメインプロトコル、ポート番号) からリソースをリクエストするとき、オリジン間 HTTP リクエストを発行します。*1

クライアントサイドの実装は、最近ではIEのごく一部のブラウザ以外で実装されていて、JavaScriptで異なるドメインのサーバへアクセスしようとした時によくエラーになっているあれです。

CORSが必要な理由

CORSのことは一旦置いておいて、Webサーバにリクエストを送った時のことを考えましょう。基本的に、Webサーバはリクエストを送ってきたクライアントのIPにレスポンスを返します。

f:id:woshidan:20181126210159p:plain

たとえば自社でAPIとWebページを開発しているときは以下のような感じになりますね。

f:id:woshidan:20181126210321p:plain

ここでもし、自社API(図中の api.example.com)に他者のホームページからリクエストが

f:id:woshidan:20181126210754p:plain

のように送信されていたらどうなるでしょうか。不正なリクエストを送ってサーバに不具合を起こされたり、顧客に本物のWebサイトと偽って表示して顧客に表示するためのコンテンツを盗まれたり、といったことは起こるかもしれません。

CORSはこのような場合に役に立つもので、CORSを用いると、Webサイトのものとは異なるドメインにリクエストを送る場合、そのリクエストが許可されていることをサーバに一度確認してからでないとPOSTなどのHTTPリクエストが送ることができなくなります*2

f:id:woshidan:20181126211356p:plain

f:id:woshidan:20181126211528p:plain

CORSの仕組み

CORSを用いると、Webサイトのものとは異なるドメインにリクエストを送る場合、そのリクエストが許可されていることをサーバに一度確認してからでないとPOSTなどのHTTPリクエストが送ることができなくなります と書きましたが、具体的にどのようにやっているのか、基本的なパターンについてさらっておきます*3

プリフライトリクエス

Content-Typetext/plain でそれ以外にめぼしいリクエストヘッダを含まないGETリクエストなど一部の単純なもの以外*4、 Webページ上のJavaScriptが外部ドメインにリクエストを送ろうとした際、Webブラウザは元のHTTPリクエストを送るのではなく、 プリフライトリクエスト というものを送信します。

この プリフライトリクエスト には

  • パスは元のリクエストのまま
  • メソッドは OPTIONS
  • リクエストヘッダには
    • リクエスト元のドメインを示す Origin
    • 元々送ろうとしていたリクエストが入る Access-Control-Request-Method
    • 元々送ろうとしていたリクエストのリクエストヘッダが入る Access-Control-Request-Headers

が含まれ、この プリフライトリクエスト を受け取ったサーバは、

  • サーバがリクエストを許可する外部サーバのドメイン一覧である Access-Control-Allow-Origin
  • サーバがリクエストを許可するリクエストメソッドの一覧である Access-Control-Allow-Methods
  • サーバがリクエストを許可するリクエストヘッダの一覧である Access-Control-Allow-Headers
  • プリフライトリクエストをしばらく有効にしておく期限を表す Access-Control-Max-Age

が含まれるレスポンスを返します。Webブラウザはこのレスポンスを見て、自分が送ろうとしていたリクエストが外部サーバから許可されているかを確認し、許可されているのを確認できたら元々送信したかったリクエストを送信します。

サーバから通信を許可されていないドメインであったり、許可されていないメソッドでリクエストしようとしている場合は、Webブラウザはリクエストの送信を断念します。

参考

*1:https://developer.mozilla.org/ja/docs/Web/HTTP/CORS

*2:GETのみなど一部確認用のリクエスト(プリフライトリクエスト)なしに送ることも可能です(単純なリクエスト)。

*3:リクエストそのものだけでなくクッキーを扱えるかどうかなどもCORSを用いて制御することができます https://qiita.com/tomoyukilabs/items/81698edd5812ff6acb34#cookie%E3%82%82%E8%A8%B1%E5%8F%AF%E3%81%97%E3%81%9F%E3%81%84%E5%A0%B4%E5%90%88

*4:詳しくは https://developer.mozilla.org/ja/docs/Web/HTTP/CORS#Simple_requests