LoginSignup
1
0

More than 1 year has passed since last update.

同一オリジンポリシー・CORSの脳内イメージを描く ~CORSエラーになるケースならないケース~

Last updated at Posted at 2023-03-10

注意

  • ざっくり脳内イメージを描くのに役立ったらうれしい。正確に理解したい方は別の文献にGO。
  • 自身の同一オリジンポリシー・CORSの理解経緯・現状の脳内イメージ(メンタルモデル)の記録が目的です。
  • この記事内の「API」はWebAPIとかRESTfulAPIとか呼ばれるようなやつを指します。
  • 有害な理解があったら指摘いただけるとありがたいです。

要約

  • フロントエンド(ブラウザ)からアクセスできるAPIは、基本的に自前のバックエンドのみ
  • よそのAPIでも許可されていればアクセス可能
  • 許可されてないAPIに対しては、そこにアクセスする自前のバックエンドを挟めばよい
  • メンタルモデルはこちら

ざっくり用語集

同一オリジン

http://example.net/indexhttp://example.net/users/5

クロスオリジン

http://example.nethttp://example.org
http://example.nethttps://example.net

クロスオリジンへのアクセス

http://example.net/index内のJSがhttp://example.orgにajax通信すること。
http://example.net/index内にあるリンクを踏んで遷移するのは該当しない。

同一オリジンポリシー

Same Origin Policy/Same-Origin Policy。SOP、同一生成元ポリシーとも。
ブラウザはajax通信に関して、基本的に同一オリジンへのものしか許可しないということ。
CSRFとかXSSとかを防ぐために決まったらしい。

CORS

同一オリジンポリシーに例外を設ける仕組み。
特定の場合にクロスオリジンにアクセスしてよい。

CORSエラー

例外が設けられていないのにクロスオリジンにアクセスしようとしたエラー。
つまり同一オリジンポリシー違反。

理解の経緯

前提・背景

前提としては普段はSpringみたいなフレームワークでJavaでMPAの業務システムを主に書いてる。
JSでDOM操作はするけどJSでのAPIアクセスやJavaでAPI作成は業務では基本的にない。

セキュリティ研修の教材で同一オリジンポリシーが出てきて調べたところ、
①自分のAPIアクセスのイメージが誤っていた
②具体性に欠ける説明が多かった
ため理解に時間がかかったので、メンタルモデルをまとめておく。

自分みたいな人向けに、実際に試せるHTMLになっています(APIの仕様変更がなければ)。

CORSエラーにならなかった例

APIはフロントエンドから自由にアクセスできると思ってたので同一オリジンポリシーが意外だった。
(言われてみるとどっかでCORSエラー踏んだことはあったはずなんだけど...)

例えば、↓の記事の内容であれば、自前でバックエンドを用意せずとも、

address.html
<html>
    <body>
        <button onclick="fetchData()">取得</button>

        <script>
            function fetchData() {
                const url = 'https://zip-cloud.appspot.com/api/search?zipcode=7830060'
                fetch(url) //fetchAPIのメソッドチェーン
                    .then((response) => response.json())
                    .then((data) => console.log(data))
            }
        </script>
    </body>
</html>

のようにフロント側で処理できるのでは?と思っていた。
(当該記事については内容を試したことがあるわけでなく、容易に使えるAPIを探していてたどり着いた)

そこで実際に上記コードを実施したところ、
image.png

あれ、エラーにならない...🤔

CORSエラーになった例

エラーになるコードを求めて↓の記事に辿り着いた

bitcoin.html
<html>
    <body>
        <button onclick="fetchData()">取得</button>

        <script>
            function fetchData() {
                const url = 'https://api.bitflyer.com/v1?product_code=BTC_JPY'
                fetch(url)
                    .then((response) => response.json())
                    .then((data) => console.log(data))
            }
        </script>
    </body>
</html>

試したところ、無事「Access to fetch at ~」というエラーになった。
うれしい。
image.png

何が起きているか

クロスドメインへのアクセスはブラウザが許可しないらしいが、どうやって実現してるのか不明だった。
(一律アクセス禁止ではCORSが実現できないので)
bitcoin.htmlについてネットワークタブを確認したところ、ステータスコードが404だった。

image.png

ステータスコードが存在している≒アクセス自体は行っている感じがしたのでその観点で調べたところ、
プリフライトリクエストと呼ばれる事前チェックをブラウザが自動的に実施していることが分かった。

その際にCORSの設定が適切にされていればチェックOKでリクエストが実行され、NGならエラーとなる。

比較のためにaddress.htmlを確認したところ、
access-control-allow-origin sec-fetch-mode: corsなどの記述がある。
この辺がポイントらしい(ざっくり理解)。
image.png

まとめメンタルモデル

※ほんのりインデントしてるのはシーケンス図っぽさを出すためです

自前のバックエンド通信

JS「バックエンドのデータ取得したいです」
 ブラウザ「同一オリジンだね。了解」
 ブラウザ「データをリクエストするよ」
  バックエンドAPI「レスポンスですどうぞ」status code: 200
 ブラウザ「レスポンスきたよ」
JS「ありがとう!」

郵便番号APIの場合

JS「郵便番号のデータ取得したいです」
 ブラウザ「クロスオリジンだね。一旦スタンバイして」
 ブラウザ「うち(ページ取得元オリジン)からアクセスしていい?」プリフライトリクエスト
  郵便番号API「だれでもウェルカム」status code: 200
 ブラウザ「データをリクエストするよ」
  郵便番号API「レスポンスですどうぞ」status code: 200
 ブラウザ「レスポンスきたよ」
JS「高知県」

ビットコインAPIの場合

JS「ビットコインのデータ取得したいです」
 ブラウザ「クロスドメインだね。一旦スタンバイして」
 ブラウザ「うち(ページ取得元オリジン)からアクセスしていい?」プリフライトリクエスト
  ビットコインAPI「ダメです」status code: 404
 ブラウザ「リクエストは無かったことにするよ」
JS「あぁぁ...」CORSエラー

あるべきビットコインAPIの利用方法

JS「ビットコインのデータをバックエンドから取得したいです」
 ブラウザ「同一ドメインだね。了解」
 ブラウザ「データをリクエストするよ」
  バックエンドAPI「データをリクエストするよ」
   ビットコインAPI「レスポンスですどうぞ」status code: 200
  バックエンドAPI「レスポンスですどうぞ」status code: 200
 ブラウザ「レスポンスきたよ」
JS「ありがとう!」

元記事内にもあるけど自前のバックエンド経由でアクセスすればいいわけですね
バックエンド「外部APIアクセスか、私も同行する」
JS「ビットコ院」

メモ

  • ajaxではなんとなくJSが直接(?)サーバにアクセスするイメージだった。HTMLをレンダリングするように、ブラウザがリクエストを実行してるというメンタルモデルが必要だった。
  • 「ブラウザが判断してる」は正しいんだけど、サーバ側の設定を読み取ってるので、広くとればサーバ側で許可を出してると言えるのかも。であれば原則全通しでサーバ側が必要に応じて拒否でもいいのかなと思ったけど、都度実装のコストとか実装漏れとか考えると今の仕組みはよくできてそう。
  • URLの例を出すときはexample.com example.org example.netが良いという知見を得た。
  • IMEがいつまでもビットコインを正しく変換してくれなかった。
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