SameSite Cookie の使用

ChromeFirefoxEdge などは、IETF の提案である Incrementally Better Cookies に沿ってデフォルトの動作が変更され、以下のようになります。

  • SameSite 属性のない Cookie は SameSite=Lax として扱われます。つまり、デフォルトの動作では、Cookie をファーストパーティのコンテキストのみに制限します。
  • クロスサイトで使用する Cookie で、サードパーティのコンテキストに含めるには、SameSite=None; Secure を指定する必要があります

サードパーティ Cookie が今後ブロックされないように、サードパーティ Cookie の属性をまだ更新していない場合は更新してください。

対応ブラウザ

  • 51
  • 16
  • 60
  • 13

ソース

クロスサイト Cookie またはサードパーティ Cookie のユースケース

サードパーティのコンテキストで Cookie を送信する必要がある一般的なユースケースやパターンは多数あります。これらのユースケースのいずれかを提供するか、依存する場合は、サービスを正しく機能させるために、ご自身またはプロバイダのいずれかが Cookie を更新していることを確認してください。

<iframe> 内のコンテンツ

<iframe> に表示される別のサイトのコンテンツが、サードパーティのコンテキストである。標準的なユースケースは次のとおりです。

  • 他のサイトから共有された埋め込みコンテンツ(動画、地図、コードサンプル、ソーシャル投稿など)。
  • 支払い、カレンダー、予約、予約機能などの外部サービスのウィジェット。
  • <iframes> がわかりにくいソーシャル ボタンや不正防止サービスなどのウィジェット。

ここで Cookie は、特に、セッション ステータスの維持、全般設定の保存、統計情報の有効化、既存アカウント ユーザーのコンテンツのカスタマイズなどに使用されます。

埋め込みコンテンツの URL がページの URL と一致しないブラウザ ウィンドウの図。
埋め込みコンテンツが最上位のブラウジング コンテキストと同じサイトのものではない場合は、サードパーティのコンテンツです。

ウェブは本質的にコンポーズ可能であるため、<iframes> は、トップレベルまたはファーストパーティ コンテキストで表示されるコンテンツを埋め込むためにも使用されます。iframe に表示されるサイトの Cookie は、すべてサードパーティ Cookie と見なされます。他のサイトに埋め込んでほしいサイトを作成し、そのサイトを動作させるために Cookie が必要な場合は、それらのサイトをクロスサイト用にマークするか、そうしたサイトなしで適切にフォールバックできることを確認する必要もあります。

サイトへの「安全ではない」リクエスト

ここでは「安全でない」と聞こえるかもしれませんが、状態の変更を意図したあらゆるリクエストを指します。ウェブでは、主に POST リクエストです。SameSite=Lax とマークされた Cookie は、リンクをクリックして別のサイトに移動する場合など、安全なトップレベル ナビゲーションで送信されます。ただし、POST を使用して別のサイトに送信する <form> 処理には Cookie は含まれません。

ページ間で移動するリクエストの図。
受信したリクエストで「安全な」メソッドが使われていれば、ページから Cookie が送信されます。

このパターンは、ユーザーをリモート サービスにリダイレクトして、戻ってくる前になんらかのオペレーション(サードパーティの ID プロバイダへのリダイレクトなど)を実行するサイトに使用されます。ユーザーがサイトを離れる前に、単一の使用トークンを含む Cookie が設定されます。この Cookie は、クロスサイト リクエスト フォージェリ(CSRF)攻撃を軽減するために、返されるリクエストでこのトークンをチェックできることを想定しています。返されたリクエストが POST 経由で届いた場合は、Cookie を SameSite=None; Secure としてマークする必要があります。

リモート リソース

ページ上のリモート リソース(<img> タグや <script> タグなど)は、リクエストと一緒に Cookie が送信されることに依存する場合があります。一般的なユースケースとしては、トラッキング ピクセルやコンテンツのカスタマイズなどがあります。

これは、fetch または XMLHttpRequest を使用して JavaScript から送信されたリクエストにも適用されます。fetch()credentials: 'include' オプションを指定して呼び出された場合、リクエストに Cookie が含まれている可能性があります。XMLHttpRequest の場合、想定される Cookie は通常、truewithCredentialsで表されます。これらの Cookie は、クロスサイト リクエストに含まれるように適切にマークする必要があります。

WebView 内のコンテンツ

プラットフォーム固有のアプリの WebView はブラウザを利用しています。デベロッパーは、アプリに影響する制限や問題がアプリの WebView にも適用されるかどうかをテストする必要があります。

Android では、プラットフォーム固有のアプリで CookieManager API を使用して Cookie を直接設定することもできます。ヘッダーまたは JavaScript を使用して設定する Cookie と同様に、クロスサイトで使用する場合は SameSite=None; Secure を含めることを検討してください。

今すぐ SameSite を実装する方法

ファーストパーティのコンテキストでのみ必要な Cookie を、必要に応じて SameSite=Lax または SameSite=Strict としてマークします。これらの Cookie にマークを付けず、デフォルトのブラウザ動作を使用して処理すると、ブラウザ間で動作に一貫性がなくなり、Cookie ごとにコンソールの警告がトリガーされる可能性があります。

Set-Cookie: first_party_var=value; SameSite=Lax

サードパーティのコンテキストで必要な Cookie はすべて SameSite=None; Secure としてマークしてください。どちらの属性も必須です。Secure を指定せずに None のみを指定すると、Cookie は拒否されます。ブラウザの実装の違いを考慮し、互換性のないクライアントに対応するで説明されている緩和戦略の一部を使用しなければならない場合があります。

Set-Cookie: third_party_var=value; SameSite=None; Secure

互換性のないクライアントを処理する

None をインクルードしてデフォルト動作を更新するこれらの変更は比較的新しいため、処理方法はブラウザによって異なります。既知の問題の一覧については、chromium.org の更新ページをご覧ください。ただし、このリストがすべてを網羅しているとは限りません。

考えられる回避策の 1 つは、次のように、各 Cookie を新スタイルと旧スタイルの両方で設定することです。

Set-cookie: 3pcookie=value; SameSite=None; Secure
Set-cookie: 3pcookie-legacy=value; Secure

新しい動作を実装しているブラウザでは、Cookie に SameSite 値が設定されます。新しい動作を実装していないブラウザは、この値を無視して 3pcookie-legacy Cookie を設定します。含まれる Cookie を処理する場合、サイトはまず新しいスタイルの Cookie の有無を確認し、見つからなかった場合は従来の Cookie にフォールバックする必要があります。

次の例は、Express フレームワークとその cookie-parser ミドルウェアを使用して、Node.js でこの処理を行う方法を示しています。

const express = require('express');
const cp = require('cookie-parser');
const app = express();
app.use(cp());

app.get('/set', (req, res) => {
  // Set the new style cookie
  res.cookie('3pcookie', 'value', { sameSite: 'none', secure: true });
  // And set the same value in the legacy cookie
  res.cookie('3pcookie-legacy', 'value', { secure: true });
  res.end();
});

app.get('/', (req, res) => {
  let cookieVal = null;

  if (req.cookies['3pcookie']) {
    // check the new style cookie first
    cookieVal = req.cookies['3pcookie'];
  } else if (req.cookies['3pcookie-legacy']) {
    // otherwise fall back to the legacy cookie
    cookieVal = req.cookies['3pcookie-legacy'];
  }

  res.end();
});

app.listen(process.env.PORT);

この方法では、冗長な Cookie の設定や、Cookie の設定と読み取りの両方の時点で変更を行う追加の作業が必要になります。ただし、ブラウザの動作に関係なくすべてのブラウザに対応し、サードパーティ Cookie が機能し続ける必要があります。

別の方法として、Set-Cookie ヘッダーの送信時にユーザー エージェント文字列を使用してクライアントを検出することもできます。互換性のないクライアントのリストを参照し、プラットフォームに適したユーザー エージェント検出ライブラリ(Node.js の ua-parser-js ライブラリなど)を使用します。この方法では 1 つの変更を行うだけで済みますが、ユーザー エージェント スニッフィングで影響を受けるすべてのユーザーが捕捉されない可能性があります。

言語、ライブラリ、フレームワークでの SameSite=None のサポート

ほとんどの言語とライブラリは、Cookie の SameSite 属性をサポートしています。ただし、SameSite=None の追加はまだ比較的最近であるため、当面は標準的な動作の回避が必要になることがあります。この動作は、GitHub の SameSite サンプル リポジトリに記載されています。

困ったときは

Cookie はウェブ上のいたるところで使用されており、特にクロスサイトのユースケースでは、サイトのどこで Cookie が設定、使用されているかについて、開発チームに完全な知識はほとんどありません。問題が発生した場合、誰もがその問題に遭遇したのは初めてかもしれませんので、遠慮なくお問い合わせください。