cookie
- Public Suffix List
SameSite=
,Domain=
概要
- 初回リクエスト(cookieなし)
- レスポンスヘッダに
Set-Cookie
- Webサーバが任意に付与
- リクエストヘッダに
Cookie
- ブラウザが保持
- 同じドメイン: 以降自動付与
機能
- 目的: 追跡、セッション維持
- 小さな識別データ
- ドメインごとに分離管理
クロスサイト・リクエスト時のポイント
- リクエストにcookieが付く場合とは?
- レスポンスのcookieが有効に保存される場合とは?
Set-Cookie
レスポンスヘッダ
名前=値
の形式- 複数ヘッダか、
;
区切りで
使える文字
- 名前: 英数字、ごく一部の記号(
_-
など) - 値: 日本語などはURLエンコード
各ヘッダごとに既定属性を付けられる
- そのcookieの動作が変わる
Cookie
リクエストヘッダ
- ブラウザが自動付与
- 属性は付かない
JavaScriptで付加・参照
HttpOnly
属性なら不可
document.cookie = 'test1 = true'
document.cookie = 'test2=abc' // 追加
document.cookie = 'test1=123' // 上書き
console.log(document.cookie) // test2=abc; test1=123
既定属性
=
のないものは名前だけで値なし
HttpOnly
: JavaScriptからアクセスできず、XSS攻撃対策として推奨Secure
: HTTPSでのみ送信
Expires=
: 有効期限(日時)- 指定なし => ブラウザ終了まで
Max-Age=
優先
Max-Age=
: 有効期限(秒数)
Domain=
: サブドメインと共有 (プロトコルやポートは指定不可)- 指定なし => 共有なし
- 指定 => 指定とそのサブドメインで共有
SameSite=
: クロスサイト・リクエスト時にcookieを有効にするか- 登録ドメインとサブドメインは「同じサイト」扱い(ただしCORS)
Path=
: パス限定のとき
Public Suffix Listとは
登録ドメインよりひとつ上となるドメインのリスト
- 2つのセクション: ICANNドメインとPRIVATEドメイン
- トップレベルドメイン、セカンドなどバラバラ
- アルゴリズムがないのでリスト
例
com
、jp
、org
co.jp
、github.io
chuo.tokyo.jp
Domain=
やSameSite=
の動作に必須
- 登録ドメインとの区切り判別
- ブラウザなどに実装
登録ドメインとは
- 「Public Suffix List」にラベルを左にひとつ追加したドメイン
- 一般に、レジストラ(例:お名前.com)経由でレジストリに登録したドメイン
例
example.com
、example.org
、example.github.io
- サブドメインは違う: sub.example.com
- 勝手に作るのであって登録はしてない
cookieの管理者の単位
example.com
とsub.example.com
: 互いにcookieを操作させても、問題なし
ここではこの文脈で「登録ドメインとサブドメイン、サブドメインどうし」を「同じサイト」と呼ぶ。
Supercookie防止
github.io
とexample.github.io
: 互いにcookieを操作させると、問題あり- => GitHubやユーザどうしが互いに干渉できてしまう
Domain=
: 「Public Suffix List」の指定は無効
- 「Public Suffix List」を除いた、自ドメインか上位ドメインを指定できる
- 例:
github.io
はリストにあるからダメ
- 例:
- FireFox 80.0.1で確認
無効になるもの(cookie未保存)
example.github.io
サーバがDomain=github.io
github.io
サーバがDomain=example.github.io
- サブドメイン指定不可
site1.example.com
サーバがDomain=site2.example.com
- 自身でも上位でもないドメイン指定不可
未指定と同様になるもの
github.io
サーバがDomain=github.io
有効なもの
site1.example.com
サーバがDomain=example.com
api.site1.example.com
サーバがDomain=site1.example.com
example.com
とは共有しない
api.site1.example.com
サーバがDomain=example.com
SameSite=
前提: クロスサイト・リクエスト時
- 以前に設定済みのcookieが残っていて
- そのドメインへ、クロスサイト・リクエスト (ここではクロスオリジンではない)
宛先ドメイン所属のcookieを付けるかどうか
- そもそも、宛先と異なるドメインのcookieは付けられない
Domain=
による共有先は可
fetch()
のcredentials
オプションでも対応が必要
SameSite=Strict
- cookieなし
- CSRF攻撃対策
SameSite=Lax
a
タグ: cookie付きfetch()
: cookieなし
SameSite=None
- cookie付き
SameSite=
における「同じサイト」
登録ドメインとサブドメイン間、サブドメインどうし
Domain=
で共有してなくてもSameSite=
にかかわらずcookie付き
- ただしCORS (混同注意)
- クロスオリジンではあるので、要件に該当したらCORS対応は必要
例: example.com
とapi.example.com
- 登録ドメインとサブドメイン
- 例: シングルページアプリとそのAPI
api.example.com
所属のcookieが残存Domain=
指定なしSameSite=Strict
example.com
からfetch()
でJSONボディをapi.example.com
へPOST
- cookie付き (「同じサイト」だから)
- レスポンス利用はCORS対応が必要 (クロスオリジンだから)
example.org
(無関係サイト)からapi.example.com
へGET
- cookie付かない (クロスサイト・リクエストだから)
レスポンスのcookieがブラウザに保存されるかどうか
サーバ側による制御
SameSite=
で- 保存後のリクエストの話ではなく
「同じサイト」のとき
- 例:
example.com
からapi.example.com
へリクエスト SameSite=
がどれでも保存
クロスサイト・リクエスト時
a
タグによるもの:SameSite=
がどれでも保存fetch()
によるもの:SameSite=None
のみ保存
fetch()
のcredentials
オプション
クライアント側による制御
- リクエスト時にcookie付けるか
- レスポンスのcookieを保存か
omit
- cookie付けない、保存しない
- 同一オリジンでも
same-origin
- 同一オリジンなら、cookie付ける、保存する
- 「同じサイト」ではない
include
- cookie付ける、保存する
- クロスオリジンでも
SameSite=None
なら - 「同じサイト」ならどれでも
クロスオリジンなら
- ACAOに加えて、ACAC対応
- プリフライト時にはcookie付かない
Authorization
リクエストヘッダにも適用
const init = {
method: 'GET',
mode: 'cors',
credentials: 'include', // クロスオリジンでもcookie送信
}
fetch('http://別オリジン.example.com/パス', init).then(
res => res.text()
).then(console.log).catch(console.error)
ACAC対応
レスポンスヘッダ: Access-Control-Allow-Credentials: true
credentials
オプションinclude
のとき、必須
プリフライト発生しないケース
- ヘッダないと、レスポンスがドキュメントに反映されずエラー
SameSite=None
のcookieは保存(FireFox)
プリフライト発生のケース
- プリフライトのレスポンスでヘッダないとき、本番のリクエストは行われずエラー
- ヘッダあっても、
Access-Control-Allow-Origin
が*
(ワイルドカード)のとき、同様にエラー
- ヘッダあっても、