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 と連携

シングル・サインオン(SSO)連携*

* SSO 連携は無料トライアルではご利用できません。プロプラン契約後からご利用できます。

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