HTTP クッキーの基本動作
HTTP クッキー(以下クッキーと書きます)とは、ウェブサーバー側がクライアント(ウェブブラウザ)側に保持させることができるデータのことをいいます。
クッキーの基本的な動作
ウェブブラウザで サイトA にアクセスする
ウェブブラウザで、例えば https://blog.isystk.com/
にアクセスします。
これは言い換えると、「https://blog.isystk.com/
というリソースを取得するためのリクエストを blog.isystk.com
というサーバー(ホスト)に送信した」ということになります。
サーバーは、要求されたリソース(ページ)を返す
サイトAのサーバーは、要求されたリソース(ページ)を返しますが、このレスポンスにおけるヘッダ部にクッキー(と呼ばれるデータ)をセットして返すことができます
Set-Cookie: SID=z1d4d3d54c7aad4d; Path=/; Domain=blog.isystk.com
Set-Cookie というフィールド名の右側に、「名前=値;」というフォーマットで各種情報がセットされます。ウェブブラウザはこの情報を保存することになります。
この場合セットされているデータは、以下の3つです。
名前 | 値 |
SID | z1d4d3d54c7aad4d |
Path(属性) | / |
Domain(属性) | blog.isystk.com |
ウェブブラウザで、再度 サイトA にアクセスする
ウェブブラウザで、再度 サイトA にアクセスすると、先程受け取ったクッキーの情報がリクエストヘッダ部に自動的にセットされて送られます。(最初に渡されたクッキーの属性値(Domain や Path)にマッチした場合のみ)
Cookie: SID=z1d4d3d54c7aad4d;
この情報をサーバー側がどう使うかは自由ですが、クライアントの同一性を保持してログイン状態を実現するために使われたりします。
クッキーの SameSite 属性について (Same-site Cookies)
SameSite 属性 は、draft-west-first-party-cookies-07 – Same-site Cookies という仕様で新しく追加された クッキーの属性で、クッキーをより安全なものにするために追加されました。
もう少し具体的に言うと、SameSite 属性の目的は、 今開いているページのドメインから、別のドメインにリクエストを送る際に、クッキーをセットするかどうか を制御することです。
例えば、https://example.com
というサイトをウェブブラウザで開いている状態で、そのページ上に https://blog.isystk.com
へのリンクが貼ってあるとします。このリンクをクリックすると、ウェブブラウザは https://blog.isystk.com
のリソースを取得するために、blog.isystk.com というサーバーに HTTP(S)リクエストを送信します。ここで、もし以前 https://blog.isystk.com
にアクセスしており、何らかのクッキーを受け取っていたのであれば、今回送信するリクエストデータの中にそのクッキーデータがセットされます(クッキーの期限が切れていなければ)。これが従来の処理です。詳細は省きますが、この動作は CSRF などの脆弱性を生む原因になります。
そこで SameSite 属性の出番です。ウェブサーバーが最初にクッキーを発行する際に SameSite属性を指定しておけば、このようなドメインを跨いだ(クロスドメイン)リクエストにそのクッキーをセットさせないことが可能になります。
先程挙げたウェブサーバーからのレスポンスヘッダにおける Set-Cookie フィールドにこの属性が追加されると、以下のようになります。
Set-Cookie: SID=z1d4d3d54c7aad4d; Path=/; Domain=blog.isystk.com; SameSite=Lax
一番後ろに、SameSite=Lax という文字列が追加されています。
SameSite にセットできる値
SameSiteの値 | 意味 |
Strict | ・他のドメインへのリクエストを送る際、Strict が指定されたクッキーはセットされません。 ・ 例えば、Aサイトでログイン中だった場合に、Bサイト上に用意されたAサイトへのリンクをクリックした場合、未ログイン状態でページの遷移が行われます。不便な面もあります。 ・ Lax よりセキュリティが高いので、銀行サイトなどでは有効な値です。 |
Lax | ・ top-level navigation(*1) で、且つ GETメソッドであれば、他のドメインへのリクエストであってもクッキーをセットします。 ・ 例えば、Aサイトでログイン中だった場合に、Bサイト上に用意されたAサイトへのリンクをクリックした場合、ログイン状態でページの遷移が行われます。 ・ POSTメソッドを使ったフォーム、imgタグ、iframe、XMLHttpRequests などによる他ドメインへのリクエストにはクッキーはセットされません。 ・ Strict より条件が緩い(lax)です。 ・ 通常は、こちらを使うことになるでしょう。 |
None | ・ 従来どおりの動作(クッキーを送る) |
※1 top-level navigation とは、アドレスバーに表示されているURLの変更が伴う遷移のことです(リクエストを送信したら、送信先のページに画面が遷移する場合)。このような遷移は、CSRF の対象にはならず安全であると判断できるためクッキーの付加が許可されているのだと思います。
Chrome 80 以降、SameSite 値が宣言されていない Cookie は SameSite=Lax として扱われる
2020年2月4日にGoogle Chrome 80がリリースされて SameSite 値が宣言されていない Cookie の扱いが変わりました。
- Chrome 79まで・・・SameSiteが未指定の場合
None
と同じになる。 - Chrome 80から・・・SameSiteが未指定の場合
Lax
と同じになる。また、SameSiteにNone
を指定する場合は、Secure属性が必須となる。
この変更によってどのような問題が発生するのか
自サイト内で SameSiteが未指定のクッキーを利用している場合、外部ドメインのサイトから自サイトにアクセスされる場合にクッキーの値が引き継がれなくなります。
- クッキーでバナー表示を出し分けしているが、Googleの検索結果など外部サイトからアクセスされた場合に制御が効かない(ただし、 ランディング後にJSでクッキーを取得したり、Ajaxで表示する場合はこの問題は発生しない )この問題はランディング時サーバーサイドでクッキーを取得した場合のみ発生する。また、その後の画面遷移(リファラが自サイトの場合)では発生しない。
- ログイン状態であった場合に、外部サイトからアクセスした際に新しいセッションIDがクッキーに保存されるため未ログインの状態となってしまう
対策方法
- クッキーを保存する際にSameSiteの値を明示的に”None”で指定する
apache側でデフォルトの属性を付加することも可能です。
Header always edit Set-Cookie (.*) "$1; secure; SameSite=none"
以下にデモページを用意しました。
https://demo.isystk.com/samesite-cookies/
いろいろな方法でアクセスしてみます。
See the Pen demo-samesite-cookies by Yoshitaka Ise (@isystk) on CodePen.