CookieのSameSite属性とは?図やサンプルコードなどで分かりやすく解説!

この記事では『CookieのSameSite属性』について、

  • CookieのSameSite属性とは
  • SameSite属性のStrictとLaxとNoneの違い
  • SameSite属性のデフォルト値
  • サンプルコードでSameSite属性の動作確認を行う

などを図やサンプルコードを用いて分かりやすく説明するように心掛けています。ご参考になれば幸いです。

CookieのSameSite属性とは

CookieのSameSite属性とは

SameSite属性は、Cookie(クッキー)を発行する際に指定できる属性の1つで、別ドメインへのリクエスト時にCookieを送信するか否かを指定するものです。

例えば、WebブラウザにはサイトBのCookieが保存されているとします。その状態において、サイトAからサイトBにリンクなどを経由して遷移する時や、サイトAからサイトBへのPOSTリクエスト時などにブラウザに保存されているサイトBのCookieを送信するか否かをSameSite属性で決めることができます。

補足

SameSite属性はSame-site Cookies draft-west-first-party-cookies-07という仕様で新しく追加されました。

SameSite属性のStrictとLaxとNoneの違い

SameSite属性のStrictとLaxとNoneの違い

CookieのSameSite属性はStrict(厳しい)Lax(緩い)None(なし)の3つの値をとります。これらの値はこれはセキュリティレベルの高さをしており、Strictが一番セキュリティレベルが高いです。

SameSite属性はHTTPレスポンスのSet-CookieヘッダでSameSite=Laxのように指定することができます。SameSite属性はオプションであるため指定しなくてもよく、指定しない場合はブラウザ側のデフォルト設定(Chrome 80からはLax)になります。

same-siteなリクエストについてはSameSite属性が何であろうとCookieは送信されます。SameSite属性によって挙動が変わるのはcross-siteなリクエストの時です。

  • SameSite=None
    • cross-siteなリクエスト時、どのリクエストでもCookieを送信します。
    • Chrome 79以下でのデフォルト値です。
    • セキュリティ上の理由から、Noneを設定するにはSecure属性(HTTPS接続必須)も同時に設定する必要があります。
  • SameSite=Lax
    • Strictより制限を緩和した設定値です。
    • Chrome 80からのデフォルト値です。
    • cross-siteなリクエスト時、Cookieの送信条件が複雑で、「サイト間の遷移(トップレベルナビゲーション)」かつ「安全なメソッドによるリクエスト時」にのみCookieを送信します。具体例を以下に示します。
      • サイト間の遷移(トップレベルナビゲーション)時にはCookieを送信する
        • サイトAからサイトBにリンクなどを経由してアクセスした際に、ブラウザに保存されているサイトBのCookieを送信します。
      • 安全でないメソッドによるリクエスト時はCookieを送信しない
        • サイトAからサイトBへのリクエストにおいて、安全でないHTTPメソッド(例えば、POSTメソッド)によるリクエストでは、ブラウザに保存されているサイトBのCookieを送信しません。
      • トップレベルナビゲーションかつ安全なHTTPメソッドによるリクエスト時はCookieを送信する
        • サイトAからサイトBへのリクエストにおいて、トップレベルナビゲーションかつ安全なHTTPメソッド(GET, HEAD, OPTIONS, TRACE)によるリクエストであれば、ブラウザに保存されているサイトBのCookieを送信します。
      • サイトをまたぐリソースの読み込み時にはCookieを送信しない
        • imgタグによる画像埋め込み、XHR(XMLHttpRequest)、iframeタグなどによるサイトをまたぐリソースの読み込み時にはCookieを送信しません。

same-siteとcross-siteの違い

same-sitecross-siteの違いについては下記の記事で詳しく説明しています。興味のある方は下記のリンクからぜひチェックをしてみてください。

トップレベルナビゲーションとは

Webブラウザのアドレスバーに表示されているURLの変更が伴う遷移のことをトップレベルナビゲーションといいます。

トップレベルナビゲーション』については下記の記事で詳しく説明しています。興味のある方は下記のリンクからぜひチェックをしてみてください。

安全なメソッドとは

RFC 7231の4.2.1では、以下の4つのメソッドを安全なメソッド(Safe Methods)として定義しています。また、RFC 6265bisの5.3.7.1でも、RFC 7231の内容に準拠して、これらのメソッドをsafeなものとして扱っています。

  • GET
  • HEAD
  • OPTIONS
  • TRACE

SameSite属性のデフォルト値

Google Chrome 80が2020年2月4日にリリースされました。その結果、SameSite属性が未設定の場合、以下がデフォルト値となります。

  • Chrome 79まで
    • SameSite=Noneとして扱われる。
    • StrictLax以外の値を指定した場合もSameSite=Noneとして扱われる。
  • Chrome 80以降
    • SameSite=Laxとして扱われる。
    • StrictNone以外の値を指定した場合もSameSite=Laxとして扱われる。
    • つまりChrome 80以降では、明示的にNoneを指定しないと、Noneとして扱われない。

サンプルコードでSameSite属性を理解する

Node.jsのhttpsモジュールを用いて、Cookieの送信に関する動作確認を行いました。動作確認では、「https://localhost:8080/」と「https://127.0.0.1:8081/」の2つのサイトを用意することで、cross-siteでのリクエストを実現しています。

あわせて読みたい

Node.jsのhttpsモジュールの使い方』については下記の記事で詳しく説明しています。興味のある方は下記のリンクからぜひチェックをしてみてください。

https://localhost:8080/」と「https://127.0.0.1:8081/」のコードを以下に示します。

const https = require('https')
const fs = require('fs')

const options = {
  key: fs.readFileSync('./private-key.pem'),
  cert: fs.readFileSync('./server-cert.pem'),
}

/*
「https://localhost:8080/」のコード
*/
https
  .createServer(options, (request, response) => {
    // cookieを設定する
    response.setHeader('Set-Cookie', [
      'SameSiteStrict=value; SameSite=Strict',
      'SameSiteLax=value; SameSite=Lax',
      'SameSiteNone=value; SameSite=None; Secure',
    ])

    response.writeHead(200, {
      'Content-Type': 'text/html; charset=utf-8',
    })

    response.write('<a href="https://127.0.0.1:8081">別ドメインのサイト(https://127.0.0.1:8081)に移動する</a>')
    response.end()
  })
  .listen(8080)

/*
「https://127.0.0.1:8081/」のコード
*/
https
  .createServer(options, (request, response) => {
    response.writeHead(200, {
      'Content-Type': 'text/html; charset=utf-8',
    })
    const html = `
    <html>
    <head></head>
    <body>
    <p>
      <a href="https://localhost:8080/">aタグでのリクエスト</a>
    </p>
    <p>
      <form action="https://localhost:8080/" method="GET">
        <button type="submit">formタグ(GETメソッド)でのリクエスト</button>
      </form>
    </p>
    <p>
      <form action="https://localhost:8080/" method="POST">
        <button type="submit">formタグ(POSTメソッド)でのリクエスト</button>
      </form>
    </p>
    <p>サイトをまたぐリソースの読み込み(imgタグ)</p>
    <img src="https://localhost:8080/">
    </body>
    </html>
    `
    response.write(html)
    response.end()
  })
  .listen(8081)

上記のコードをNode.jsで実行して(node ファイル名で実行できます)、HTTPSサーバを起動します。その後、Webブラウザで「https://localhost:8080/」にアクセスしてリクエストを行います。「https://localhost:8080/」にリクエストをした際のレスポンスヘッダを以下に示します(無関係なフィールドについては省略しています)。

HTTP/1.1 200 OK
Set-Cookie: SameSiteStrict=value; SameSite=Strict
Set-Cookie: SameSiteLax=value; SameSite=Lax
Set-Cookie: SameSiteNone=value; SameSite=None; Secure

https://localhost:8080/」にアクセスし、デベロッパーツールで確認すると、以下に示すように、上記に示す3種類のCookieがWebブラウザに保存されていることがわかります。

サンプルコードでSameSite属性を理解する

https://localhost:8080/」の画面に表示されているリンクで「https://127.0.0.1:8081/」に移動します。Cookieの送信検証は全て、「https://127.0.0.1:8081/」から「https://localhost:8080/」にリクエストを送ることで行います(ドメインが異なるためcross-siteなリクエストになります。つまり、SameSite属性がStrictのCookieは常に送信されません)。

aタグによるリクエストでのCookieの送信検証

aタグによるリクエストを行った際のリクエストヘッダを以下に示します(無関係なフィールドについては省略しています)。

GET / HTTP/1.1
Cookie: SameSiteLax=value; SameSiteNone=value
Host: localhost:8080
Referer: https://127.0.0.1:8081/

GETメソッドなので安全なメソッド(Safe Methods)です。また、「https://127.0.0.1:8081/」がリクエストしたURLは「https://localhost:8080/」であり、アドレスバーにも「https://localhost:8080/」が表示されているので、トップレベルナビゲーションです。
以上より、SameSite属性がNoneのCookieだけでなく、LaxのCookieも「https://localhost:8080/」に送信されています。

formタグによるリクエストでのCookieの送信検証

formタグ(GETメソッド)によるリクエストを発行した際のリクエストヘッダを以下に示します(無関係なフィールドについては省略しています)。

GET / HTTP/1.1
Cookie: SameSiteLax=value; SameSiteNone=value
Host: localhost:8080
Referer: https://127.0.0.1:8081/

aタグによるリクエストと同じで安全なメソッド(Safe Methods)かつトップレベルナビゲーションなので、SameSite属性がNoneのCookieだけでなく、LaxのCookieも「https://localhost:8080/」に送信されています。

formタグ(POSTメソッド)によるリクエストを発行した際のリクエストヘッダを以下に示します(無関係なフィールドについては省略しています)。

POST / HTTP/1.1
Cookie: SameSiteNone=value
Host: localhost:8080
Referer: https://127.0.0.1:8081/

トップレベルナビゲーションですが、POSTメソッドなので、SameSite属性がNoneのCookieのみが「https://localhost:8080/」に送信されています。

imgタグによるリクエストでのCookieの送信検証

imgタグによるリクエストを発行した際のリクエストヘッダを以下に示します(無関係なフィールドについては省略しています)。

GET / HTTP/1.1
Cookie: SameSiteNone=value
Host: localhost:8080
Referer: https://127.0.0.1:8081/

GETメソッドなので安全なメソッド(Safe Methods)ですが、トップレベルナビゲーションではないので、SameSite属性がNoneのCookieのみが「https://localhost:8080/」に送信されています。

今回、aタグ・formタグ・imgタグでCookieの送信検証をしてきましたが、sameSite属性による動作の挙動をまとめると以下のようになります。今回動作確認していないものも含めています。

same-sitecross-site
strictLaxNonestrictLaxNone
aタグ×
imgタグ××
ifameタグ××
formタグ
(GETメソッド)
×
formタグ
(POSTメソッド)
××
XMLHttpRequest××
Fetch××

上記の表のポイント

  • same-siteなリクエスト
    • SameSite属性が何であろうとCookieが送信される。
  • cross-siteなリクエスト
    • SameSite属性がStrictの場合、どのリクエストでもCookieが送信されない。
    • SameSite属性がNoneの場合、どのリクエストでもCookieが送信される。
    • SameSite属性がLaxの場合、Cookieの送信条件が複雑で、安全なメソッド(Safe Methods)かつトップレベルナビゲーションの時のみCookieが送信される。

本記事のまとめ

この記事では『CookieのSameSite属性』について、以下の内容を説明しました。

  • CookieのSameSite属性とは
  • SameSite属性のStrictとLaxとNoneの違い
  • SameSite属性のデフォルト値
  • サンプルコードでSameSite属性の動作確認を行う

お読み頂きありがとうございました。