yamory Blog

CORS & Same Origin Policy 入門

CORS(オリジン間リソース共有) はWebセキュリティの観点において、Web開発・Web制作に関わる方すべてが理解しておくべき仕組みです。
本記事では、このCORSについての概要と一般的な設定方法について解説していきます。

CORS とは

CORS(Cross-Origin Resource Sharing, オリジン間リソース共有)は、自身のオリジンから見た、別のオリジン(クロスオリジン)からのリクエストを許可するか決定する仕組みです。(特定条件下で、通常は後述するSame Origin-Policyの機構により、ブロックされます)

summary of cors
CORSの概要

WebページのHTMLには、画像やビデオ、CSS、JavaScript、iframeなどをHTMLタグを使って自由に埋め込むことができます。
ただし、別ドメインへ向けた一部の非同期的なリクエスト(正確にはクロスオリジンのリソース使用)については「同一オリジンポリシー」と呼ばれる仕組みにより、デフォルトではアクセスできないようブロックされています。

Webブラウザのコンソールで「Access to XMLHttpRequest at ○○○ from origin ●●● has been block by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.」というエラーが発生します。
CORSポリシーによりリクエストがブロックされた例(Chrome DevTools)

この仕組みにより「XSS(クロスサイトスクリプティング)」の脆弱性については、問題が他のオリジンに波及しないようになっており、また「CSRF(クロスサイトリクエストフォージェリ)」の脆弱性についても、非同期リクエストを利用したケースにおいては、容易に起きないようになっているともいえるでしょう。

オリジンとはなにか

CORSを理解するうえで必要なのが「オリジン」の概念です。

オリジンは「スキーム + ホスト + ポート番号」の組み合わせをいいます。
ドメインと似ていますが、プロトコルとポート番号を含んでいる点がドメインとは異なります。

2つのオリジンの「スキーム + ホスト + ポート番号」が一致している場合、両者は「同一のオリジンである」とみなされます。

definition of the origin
オリジンの定義

Same Origin Policy(同一オリジンポリシー)とはなにか

Same Origin Policy(同一オリジンポリシー、同一生成元ポリシー)は、あるオリジンで読み込まれたリソース(HTML, スクリプト, Cookieなど)が、異なるオリジン(クロスオリジンとも呼ばれる)のリソースとの通信・アクセスを制限する、Webブラウザが持つセキュリティ機能です。

異なるオリジンからロードされたリソースへのアクセスをブロックすることにより、元オリジンで読み込まれたリソースに対して悪い影響を及ぼさないようにすることを目的としています。
本記事では、以後Same Origin Policyを「同一オリジンポリシー」で説明します。

Same Origin Policy(同一オリジンポリシー)が適用されるケース、適用されないケース

同一オリジンポリシーは、設計当初はJavaScriptによるAjaxやWebフォントなど、ネットワーク通信に関わる部分のみ適用されていましたが、その後の拡張でローカルファイルやブラウザ特権のリソースなど、幅広い範囲に適用されるようになりました。(誤解が発生しやすくなるため、本記事では主にネットワーク通信における同一オリジンポリシーについてのみ記載します)

同一オリジンポリシーが適用されるケース

以下のように、非同期でリソースを読み込もうしている場合は、同一オリジンポリシーによる制約が適用されます。

  • JavaScriptでの非同期通信(Ajax, いわゆるXMLHttpRequestやFetch APIを使った通信)
  • CSSでの @font-face を使ったWebフォントの読み込み
  • WebGLテクスチャの読み込み
  • canvasの drawImage() を用いた画像やビデオフレームの描画
  • 画像からCSSシェイプの生成

これらは通常、後述する「シンプルリクエスト」では無いケースであり、同一オリジンポリシーによって制限が発生するリクエストです。

同一オリジンポリシーが適用されないケース

  • <img>タグのsrc属性で読み込んだ画像
  • <link>タグのhref属性で読み込んだCSS
  • <script>タグのsrc属性で読み込んだJavaScript
  • <form>タグの action 属性で設定した送信先URL
  • <video>, <audio> タグのsrc属性で読み込んだマルチメディアファイル
  • <iframe>, <frame> タグのsrc属性での別サイトコンテンツの読み込み
    • X-Frame-Options の設定によっては読み込みがブロックされます
    • JavaScriptを用いてiframe内のドキュメントにアクセスすることはできません

異なるオリジンへアクセスできるようにCORSを設定する

前項でご紹介した「同一オリジンポリシー」という制約により、非同期では別オリジンにあるリソースにはアクセスできないことがわかりました。

ただ、Web開発・制作を行うにあたり、異なるオリジンにあるリソースへアクセスしたいケースもあるかと思います。
例えば、Webサービスがサブドメインごとにコンテンツが分かれていて、別のサブドメインのコンテンツにアクセスしたいケースなどです。

こういったケースにおいて、CORS(オリジン間リソース共有)を設定することにより、同一オリジンポリシーの制約を回避・緩和することができます。

同一オリジンポリシーとCORSという文脈の中では、「シンプルリクエスト」「プリフライトリクエスト」という2種類のリクエストが登場します。

同一オリジンポリシーによって制限が発生するかは、通常「シンプルリクエスト」であるかどうかで決まります。シンプルリクエストでなかった場合、ブラウザのセキュリティチェックフローに則って、プレフライトリクエストを交えたリクエストフローが実施されます。

flowchart
シンプルリクエスト/プリフライトリクエスト非同期通信のフローチャート(Wikipedia より引用)

シンプルリクエストであるかどうかは、次の表の条件に当てはまるかで決まります。

シンプルリクエストのケース 以下の条件すべてに合致する
  • リクエストのメソッドがGET, POST, HEADのいずれかに設定されている場合
  • Content-Type ヘッダーが application/x-www-form-urlencoded , multipart/form-data , text/plain のいずれかに設定されている場合
  • 以下の一般的なヘッダのみで、特殊なヘッダが存在しない場合
    • Accept
    • Accept-Language
    • Content-Language
    • DPR
    • Downlink
    • Save-Data
    • Viewport-Width
    • Width
プリフライトリクエストが発生するケース 以下の条件のいずれかに合致する
  • リクエストのメソッドがGET, POST, HEADでない場合
  • Content-Type ヘッダーが application/x-www-form-urlencoded , multipart / form-data , text/plain のいずれでもない場合
  • シンプルリクエストで許可されたリクエストヘッダ以外が付与されている場合

一例として「api.example.com 上のWebアプリケーションにおいて、www..example.com からのリクエストを許可したい」という前提のもと、代表的な設定方法をご紹介します。

シンプルリクエスト

シンプルリクエストの場合、特別何かを実施しなければ外部オリジンへの通信が行えないといったことはありません。

プリフライトリクエスト

プリフライトリクエストは、実リクエストを行う前に OPTIONS メソッドを用いて別オリジンのサーバと通信を行い、許可される通信元であるかどうかを事前に確認するHTTPリクエストであり、Webブラウザが自動的にプリフライトリクエストを行います。

プリフライトリクエストのレスポンスが確認できた後に、実際のリクエストが送信されるようになっています。また、プリフライトリクエストのレスポンスが許可されていないことを示していた場合は、実際のリクエストは送信されず、リクエストが終了します。

ヘッダーを追加する

クライアントサイドでの設定

クライアントサイド(Webブラウザ→Webサーバ)では、リクエストヘッダーに以下が追加されます。基本的にはブラウザが自動で付与するため、特段何かを実施する必要はありません。

Origin: https://api.example.com // アクセスを許可するオリジン
Access-Control-Request-Method: POST // 許可するメソッド
Access-Control-Request-Headers: X-PINGOTHER, Content-Type // 許可するリクエストヘッダ

しかし Fetch APIを用いてリクエストを送信する場合は、オプションの設定が必要です。

fetch('https://api.example.com/users’, { mode: 'cors' });

サーバサイドでの設定

サーバサイドでは、以下をレスポンスヘッダーを付加します。

Access-Control-Allow-Origin: https://www.example.com
Access-Control-Allow-Methods: POST, GET, OPTIONS, PUT

Access-Control-Allow-Origin レスポンスヘッダーを付与することにより、オリジンを越えたアクセスを許可することをWebブラウザに知らせることができます。

また Access-Control-Allow-Methods によって、クロスオリジンでの通信で許可されるリクエストメソッドを定義し、ブラウザに知らせることができます。
最低限上記二つを実施すればクロスオリジン間の通信が行えます。

CORSが適用されるリクエストにおいて、デフォルトではCookieやBasic認証に関わる情報は送信されません。

クロスオリジンのAjaxリクエストにおいて、Cookieの送信、Basic認証といったクレデンシャル情報(Creadentials)を付加したい場合、クライアントサイド(JavaScript)側でwithCredentialsと呼ばれるオプションをあらかじめJavaScript側で付与しておく必要があります。

以下は代表的なライブラリであるjQueryとaxiosおよびXMLHttpRequestでの設定例です。

jQuery の場合

$.ajax({
  url: "//api.example.com/users",
  xhrFields: { withCredentials: true }
});

axios の場合

axios.get('//api.example.com/users', { withCredentials: true });

XHLHttpRequest (XHR) の場合

var xhr = new XMLHttpRequest();
xhr.open('PUT, 'https://api.example.com/users');
xhr.withCredentials = true;
xhr.send(null);

また、Cookie 操作を行う場合、バックエンドの当該レスポンスヘッダーに Access-Control-Allow-Credentials: true を付加する必要になります。
※Credentialsを必要とするリクエストの場合、Access-Control-Allow-Origin で許可するオリジンを明示的に指定する必要がありますのでご注意ください。( * は使えません。)

また、クロスオリジンのサーバーによってセットされたCookieは、クライアント側のJavaScriptからはアクセスできません。あくまでクロスオリジンのサーバでCookieが必要な場合にのみ利用するようにしましょう。

CORS の設定を支援するライブラリ

前項ではCORSで具体的に追加するHTTPヘッダーについてご紹介しましたが、代表的なフレームワークにおいてヘッダーの追加を支援するライブラリもありますので、参考までにご紹介します。

フレームワーク(言語) CORS設定を支援するライブラリ
Ruby on Rails (Ruby) rack-cors
Laravel (PHP) laravel-cors
Django (Python) django-cors-headers
Spring (Java, Kotlin) 標準でCORSサポートあり
公式サイトの Enabling Cross Origin Requests for a RESTful Web Serviceを参照
Play Framework (Scala, Java) 標準でCORSサポートあり
公式サイトの Cross-Origin Resource Sharing - CORS Filterを参照

さいごに

CORSおよび同一オリジンポリシーは、WebブラウザにおけるWebセキュリティの肝となるものです。
ただし、CORSはあくまで同一オリジンポリシーの制約の中で別のオリジンへアクセスを行うための仕組みであり、セキュリティの問題を本質的に解決するものではありません。

CORSが設定できていたとしても「XSS(クロスサイトスクリプティング)」や「CSRF(クロスサイトリクエストフォージェリ)」が発生するリスクはあります。
Web開発者・制作者・セキュリティ担当者は今一度、CORSおよび同一オリジンポリシーについて理解し、適切な脆弱性対策と実装を行うよう、心がけていきましょう。

最後になりますが、Webアプリケーションで用いられるフレームワーク・ライブラリに潜む脆弱性については、yamoryを用いることで検出が可能です。ぜひ一度お試しいただければ幸いです。

オープンソース脆弱性管理ツール yamory

  • 利用中のOSSを抽出し
    脆弱性を自動スキャン
  • 脆弱性への対応優先度を自動で分類
  • 組織規模に合わせたプランを選択可能

フリー

¥0

個人向け

1チームまでの開発チーム作成

yamory を使いはじめる

プロ

有料料金はお問い合わせください

中〜大規模組織向け

無制限の開発チーム作成

開発チームを俯瞰できるセキュリティチーム機能

危険な脆弱性の Slack 連携通知機能

Jira Cloud と連携

30日間の無料トライアルを開始