CORS
ドキュメント
CORS: クロス・オリジン・リソース・シェアリング
- リクエスト先が同一オリジンでなく、レスポンス利用制限がある場合
- レスポンスヘッダで許可する仕組み
- 特に
fetch()
のとき
許可: レスポンスヘッダAccess-Control-Allow-Origin
- オリジンを指定 (以降、ACAO指定)
画面遷移: a form
タグ
CORS対象外
- シェアしないから
<form id="myform" action="別オリジン.example.com/パス" method="post">
<input type="text" name="key" value="value">
<input type="text" name="キー" value="バリュ">
</form>
特定リソース埋め込み: img link script
など
ACAO不要
- 単純な
GET
<img src="別オリジン.example.com/パス.png">
<link href="別オリジン.example.com/パス.css" rel="stylesheet">
<script src="別オリジン.example.com/パス.js"></script>
- crossorigin属性指定 => ACAO指定なかったときレスポンス利用不可
script
タグ: エラー情報の制限
別リソース内部利用: fetch()
ACAO指定
GET
でも: セッション情報を含む可能性大
プリフライトある場合
- 加えて、
OPTIONS
メソッドでのレスポンスも必要
プリフライト
リクエストが特定の条件に合致すると、(そのリクエスト前に)
- プリフライト: ブラウザが自動で
OPTIONS
メソッドでアクセス - レスポンスヘッダ: ACAO指定がある
- なければ本来のリクエストは行われない
- 本来のリクエスト
プリフライトになる主な例 (いずれか)
- セーフリスト以外のヘッダを手動設定
- 例:
Authorization
などや独自ヘッダ - セーフリストの例:
Accept Accept-Language Content-Language
など
- 例:
Content-Type
ヘッダがapplication/json
GET POST HEAD
メソッド以外
fetch()
でプリフライトになる例
- JSON送信
const json = {キー: 'バリュ', key: 'value'}
const init = {
method: 'POST',
mode: 'cors',
body: JSON.stringify(json), // これだけではtext/plain扱い
headers: {'Content-Type': 'application/json'},
}
fetch('http://別オリジン.example.com/パス', init).then(
res => res.text()
).then(console.log).catch(console.error)
別ドキュメント埋め込み: iframe
- 単純な
GET
- 画面遷移せず、
iframe
部分にドキュメント埋め込み
ACAO不要
- 無断で埋め込みできてしまう
クロス・オリジンのdocument
に直接アクセスできない
iframe
側のwindow
オブジェクト:iframe要素.contentWindow
- その
document
などほとんどのプロパティにアクセスできない- 可能な例:
postMessage close focus
など
- 可能な例:
<iframe id="子" src="http://別オリジン.example.com/パス" width="100" height="100"></iframe>
<script>
window.onload = () => {
const childWindow = document.getElementById('子').contentWindow
console.log(childWindow.focus, childWindow.postMessage) // アクセス可
childWindow.document // エラー
// Uncaught DOMException: Permission denied to access property "document" on cross-origin object
}
</script>
iframe
との情報交換はpostMessage()
を使う
送信側:
postMessage()
実行 ==> 受信側:message
イベントをリッスンiframe
から見た親window
:window.parent
送信側
- 送信先の
window
オブジェクトのpostMessage()
- 引数2: 送信先オリジン
- 自己が無断で
iframe
にされても、親は受信できない
- 自己が無断で
window.onload = () => {
const targetWindow = window.parent // 送信先が親のとき
const targetOrigin = 'http://送信先ドメイン.example.com'
targetWindow.postMessage('メッセージ', targetOrigin)
}
受信側
message
イベントに登録event.data
: 受信したメッセージevent.origin
で送信元を確認- 自己が無断で
iframe
にされても、メッセージを無視
- 自己が無断で
window.addEventListener("message", event => {
const srcOrigin = 'http://送信元ドメイン.example.net'
if (event.origin !== srcOrigin) return // 送信元が違えば無視
console.log(event.data)
})