SameSite Cookie の使用

ChromeFirefoxEdge などのブラウザは、IETF の提案である Incrementally Better Cookies に沿ってデフォルトの動作を変更し、次のようにします。

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

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

対応ブラウザ

  • Chrome: 51.
  • Edge: 16.
  • Firefox: 60.
  • Safari: 13。

ソース

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

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

<iframe> 内のコンテンツ

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

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

Cookie は、セッション状態の維持、一般的な設定の保存、統計情報の有効化、既存のアカウントを持つユーザー向けのコンテンツのパーソナライズなどのために使用されることがあります。

埋め込みコンテンツの URL がページの URL と一致しないブラウザ ウィンドウの図。
埋め込まれたコンテンツがトップレベルのブラウジング コンテキストと同じサイトからのものでない場合は、サードパーティ コンテンツです。

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

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

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

リクエストがページ間で移動する図。
受信リクエストが「安全な」メソッドを使用している場合、ページは Cookie を送信します。

このパターンは、ユーザーをリモート サービスにリダイレクトして、リダイレクト先でオペレーションを実行してから戻ってくるサイトに使用されます(サードパーティ ID プロバイダにリダイレクトする場合など)。ユーザーがサイトを離れる前に、1 回限りのトークンを含む Cookie が設定されます。このトークンは、リクエストの返信時に確認してクロスサイト リクエスト フォージェリ(CSRF)攻撃を軽減することを目的としています。返されたリクエストが POST で送信された場合、Cookie を SameSite=None; Secure としてマークする必要があります。

リモート リソース

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

これは、fetch または XMLHttpRequest を使用して JavaScript から送信されるリクエストにも適用されます。credentials: 'include' オプションfetch() が呼び出されると、リクエストに 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

新しい動作を実装しているブラウザは、SameSite 値を使用して Cookie を設定します。新しい動作を実装していないブラウザは、その値を無視して 3pcookie-legacy Cookie を設定します。サイトは、含まれる 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 はウェブ上のあらゆる場所で使用されています。特にクロスサイトのユースケースでは、自社のサイトがどこで設定し、使用されるかを完全に把握している開発チームはほとんどいません。問題が発生したときは、おそらく初めて経験する人もいるかもしれないので、遠慮なくお問い合わせください。