🦔

3rd Party Cookieの挙動をローカル環境で検証する

2024/09/11に公開

株式会社バニッシュ・スタンダードでバックエンドエンジニアをやっている西谷です。
早いもので、いつの間にか入社して2ヶ月が経過しました。

今回はローカル環境でHTTPSで動作するサーバーを構築してサイト間通信でのCookieの送受信の挙動を検証してみたので、共有します。

やりたいこと

以下のようなサイト間での通信でCookieの送受信が行えるかを、ローカル環境上で検証するのが今回の目標です。

  • 1st.example.com:ファーストパーティサイト
  • 3rd.example.net:サードパーティAPI

以降、1st.example.comを1st、3rd.example.netを3rdと呼ぶことにします。

課題

サクッと検証したいところですが、以下のような課題が存在します。

  • サイト間通信でのCookie送受信となるため、SameSite=Noneの指定が必要
  • SameSite=None の場合、Secure属性の指定が必須検証のためにローカル環境のHTTPS化が必要

ローカル環境の準備

検証に必要なコードと、HTTPSで動作するサーバーを準備します。

ローカルサーバーの準備

今回はNode.jsでローカルサーバーを立てます。

セットアップ

サーバーで使用するExpressをインストールします。

$ npm init
$ npm install express

サーバーのコードを作成

今回は楽をするため、1つのコードに1st,3rd用のコードを混在させます。
以下の3つのエンドポイントを定義してあります。

  • /(1st用)
  • /write-cookie(3rd用)
  • /read-cookie(3rd用)
index.js
const express = require('express');
const app = express();
const port = 3000;

// CORSの設定
const setCorsHeaders = (res) => {
  res.setHeader('Access-Control-Allow-Origin', 'https://1st.example.com');
  res.setHeader('Access-Control-Allow-Credentials', true);
}

// -------------------- 1st.example.com用 --------------------
// ユーザーが直接訪れるサイトのHTMLを配信
app.get('/', (req, res) => {
  res.sendFile(`${__dirname}/index.html`);
})

// -------------------- 3rd.example.net用 --------------------
// Cookieを作成
app.get('/write-cookie', (req, res) => {
  setCorsHeaders(res);
  
  res.cookie(
    'cookie',
    'value',
    {
      sameSite: 'none',
      secure: true,
    }
  );
  res.json({});
})

// クライアントから送信されたCookieの内容を読み取って返す
app.get('/read-cookie', (req, res) => {
  setCorsHeaders(res);

  res.json(req.headers.cookie);
})

app.listen(port, () => {
  console.log(`Example app listening on port ${port}`);
})

1st用のHTML

1stにアクセスすると返ってくるHTMLです。
scriptタグ内のJSで、/write-cookie を叩いてレスポンスを受け取った後に read-cookie を叩くようになっています。

index.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>ファーストパーティサイト</title>
  <script type="text/javascript">
    const sendRequest = (path, method, onLoad = undefined) => {
      const xhr = new XMLHttpRequest();
      if (onLoad !== undefined) {
        xhr.addEventListener("load", onLoad);
      }
      xhr.open(method, `https://3rd.example.net/${path}`);
      xhr.withCredentials = true;
      xhr.send();
    }
    
    const reqListener = () => {
      sendRequest('read-cookie', 'GET');
    }
    
    sendRequest('write-cookie', 'GET', reqListener);
  </script>
</head>
<body>
ファーストパーティサイト
</body>
</html>

hostsの設定

検証で使用するドメインに接続できるように、hostsを編集します。

hosts
127.0.0.1	1st.example.com
127.0.0.1	3rd.example.net

https-portalを使用するための定義

上記のローカルサーバーを、https-portalでHTTPS化します。

docker-compose.yml
services:
  https-portal:
    image: steveltn/https-portal:1
    ports:
      - '80:80'
      - '443:443'
    environment:
      DOMAINS: '1st.example.com -> http://host.docker.internal:3000, 3rd.example.net -> http://host.docker.internal:3000'
      STAGE: 'local'

Cookieの挙動を検証

ローカル環境の準備ができたので、準備した環境を使って検証を進めていきます。

起動

ローカルサーバーとhttps-portalを起動します。

$ node index.js
$ docker compose up

挙動を確認

https://1st.example.com/ に接続して、サイト間通信のCookieの挙動を確認します。

Cookieの書き込みができていることを確認

Dev-ToolsのApplicationタブで、サイト間通信でのCookieの書き込みが成功していることを確認します。
https://3rd.example.net/write-cookie でSet-Cookieが行われています)

Cookieの読み取りができていることを確認

Dev-ToolsのNetworkタブで、サイト間通信でのCookieの読み込みが成功していることを確認します。(https://3rd.example.net/read-cookie では、読み込んだCookieの内容をそのままレスポンスで返しています)

まとめ

https-portalを使うことで、ローカル環境のHTTPS化を簡単に行うことができました。
実際にサーバーを動かして検証することで、サイト間通信でのCookie送受信に必要な設定について理解を深めることができました!

Discussion