Web開発
クッキーなしでユニークビジターを追跡する方法
Tracking unique visitors without cookies (inmargin.io)
要約
この記事では、プライバシー規制やブラウザの制限により使用が困難になっているクッキーに代わる、ユニークビジター追跡の様々な手法を解説しています。クッキー、localStorage、フィンガープリンティングといった従来の技術の限界と、それらを回避するためのサーバーサイドでのID導出や、有効期限付きのハッシュ化といったプライバシー重視のアプローチについて掘り下げています。
全文翻訳
「ユニークビジター」とは、メトリクスの衣装を着たアイデンティティの問題です。ページビューではなく人を数えるには、2つのリクエストが同じ人物から来たことを認識する必要があります。25年間、その答えはクッキーでした。ランダムなIDを一度設定し、それを永遠に読み取るのです。それは見事に機能しますが、まさにそのために、現在では同意バナー、ePrivacyの記録、そしてそれを排除するブラウザや拡張機能の増加を招いています。
Marginはクッキーを設定せず、localStorageに何も書き込まず、フィンガープリンティングライブラリもロードしません。それでもユニークビジターを表示します。この記事では、クッキーからソルトハッシュに至るまで、アイデンティティ技術の全メニューを歩き、Marginがどれを選んだかを説明し、そのコストについて正直に述べます。なぜなら、トレードオフは現実のものであり、ほとんどが十分に広告されていないからです。
アイデンティティ技術のメニュー
すべての分析ツールは同じ質問に答えます。このリクエストは以前見たブラウザからのものか?答えが得られる場所はごくわずかで、それぞれが精度とプライバシーの線上のどこかに位置しています。
戦略1:クッキー
古典的な方法です。初回訪問時にランダムなIDを生成し、ブラウザに渡します。ブラウザは、期限が切れるまで、すべてのリクエストでそれを忠実に返します。
// ファーストパーティ分析クッキー、GAスタイル
let id = readCookie("_uid");
if (!id) {
id = crypto.randomUUID();
document.cookie = `_uid=${id}; Max-Age=63072000; SameSite=Lax; Path=/`;
}
// 2年間の安定したアイデンティティ:日をまたぐユニーク数、リテンションカーブ、アトリビューションウィンドウ。すべてがうまく機能します。
サードパーティクッキー(ベンダーのスクリプトをロードするすべてのサイト間で共有されるID)は事実上死んでいます。SafariとFirefoxは何年も前からブロックしており、Chromeでは大幅に制限されています。上記のファーストパーティクッキーのようなクッキーは技術的にはまだ機能しますが、法的には訪問者のデバイスに保存された状態であり、まさにePrivacyルールが同意の背後にゲートを設けているものです。それゆえにバナーが存在します。同意を得たとしても、SafariのITPはスクリプトで設定されたクッキーを7日間に制限するため、「2年間のアイデンティティ」は、トラフィックの大部分で静かに1週間に変わります。
戦略2:localStorage
誘惑的な回避策:「ルールはクッキーについて言っているので、IDは別の場所に保管しよう」。
// 同じアイデア、別の引き出し
let id = localStorage.getItem("uid");
if (!id) {
id = crypto.randomUUID();
localStorage.setItem("uid", id);
}
これは何も購入しません。ePrivacy指令の第5条(3)は、ユーザーのデバイスに「情報を保存すること、または既に保存されている情報にアクセスすること」をカバーしています。クッキーという言葉は一度も言及されていません。localStorage、sessionStorage、IndexedDB、ETagの再出現のようなキャッシュトリック:すべて同じルール、同じ同意要件です。ストレージAPIを切り替えてバナーを削除することは、戦略ではなくコンプライアンスのバグです。
戦略3:フィンガープリンティング
IDを保存できない場合は、ブラウザ自体をIDにします。十分な数の奇妙な組み合わせがユニークになります。
// 疑似コード。何も保存されません。ブラウザの形状が識別子になります。
const fingerprint = await sha256(
[
navigator.userAgent,
navigator.language,
screen.width,
screen.height,
Intl.DateTimeFormat().resolvedOptions().timeZone,
await canvasQuirks(), // GPU + フォントレンダリングの違い
await audioQuirks(), // DSP浮動小数点数の違い
].join("|"),
);
これはメニューの中で最も暗いコーナーです。耐久性があり(保存されたことのないものをクリアすることはできません)、ユーザーには見えず、研究によるとサイトはクッキーの同意が拒否された場合にまさにそれを使用します。規制当局は、この方法でデバイス情報にアクセスすることを同じePrivacyルールの範囲内と見なしており、ブラウザはそれを積極的に妨害しています。SafariとFirefoxは、それが読み取るAPIを意図的にぼかしています。フィンガープリンティングライブラリでクックルレスユニークを「解決」する分析ベンダーは、単に問題をより悪い印象で再構築しただけです。
戦略4:サーバーでIDを導出する
上記のすべては、訪問者のデバイスに何かを保存するか、またはデバイスから何かを読み取ります。残りのオプションは、サーバーが既に受け取ったものだけを使用することです。すべてのHTTPリクエストは、IPアドレスとユーザーエージェントを運んで到着します。それらを一緒にハッシュすると、少なくともIPまたはブラウザが変わるまで、同じ人物が同じ値を生み出します。生のままでは、それはサーバーサイドに移動したフィンガープリンティングであり、永続的な仮名IDです。プライバシー重視のツールが収束した洞察は、ハッシュを期限切れにすることです。Marginが属するファミリーはこれであり、独自のセクションに値します。
戦略5:アイデンティティを諦める
言及する価値があります。一部のツールは単にユニーク数をカウントしません。ページビュー、リファラー、およびヒューリスティックな「セッション」(30分のギャップで新しいセッションが開始される)は、アイデンティティをまったく必要としません。これは最もプライベートなオプションであり、完全に正直なものですが、「何人」は通常、分析に最初に尋ねられる質問なので、ほとんどのツールはここで止まりません。
業界のトリック:期限切れのハッシュ
すべての真剣なクックルレス分析ツールは、同じアイデアに収束しました。サーバーが既に認識しているものからビジターIDを導出し、それを単一の日にスコープして、トラッキングIDにならないようにします。Plausibleはhash(daily_salt + domain + ip + user_agent)を計算します。ソルトは24時間ごとにローテーションされるランダムな値で、古いソルトは削除されるため、昨日のIDは今日のIDとリンクできません。攻撃者もPlausible自身もできません。Fathomは、IP、ユーザーエージェント、ホスト名、およびサイトスコープのソルトをSHA-256でハッシュし、IPをすぐに破棄します。GoatCounterは、サイト+IP+User-Agent → ランダムIDのマッピングを最大8時間メモリに保持し、永続化するのはランダムIDのみです。
ソルトバージョンは次のようになります。
// 疑似コード:Plausibleスタイルのスキーム
const salt = await saltStore.today(); // ランダムな128ビット以上、24時間ごとにローテーション、古い値は永久に削除
const visitorId = sha256(`${salt}${domain}${ip}${userAgent}`); // visitorIdを保存。ipまたはuserAgentは保存しない
共通のプロパティ:
同じ人物は1日のうちに同じIDを生成するため、日ごとのユニーク数は正確です。
入力(IP、ユーザーエージェント)は保存されず、導出されたIDのみが保存されます。
IDは日とともに期限切れになるため、プロファイルを作成するための永続的なものはありません。
そして、そのスニペットに隠された1つの運用要件に注意してください:saltStore。どこかで、単一の真実の情報源がソルトを生成し、ハッシュを実行するすべてのサーバーと共有し、1日後に確実に破棄する必要があります。それを念頭に置いてください。
Marginが計算するもの
Marginのバージョンは、インジェストハンドラで実行されるのに十分小さいです。
const day = new Date().toISOString().slice(0, 10);
const digest = await crypto.subtle.digest(
"SHA-256",
new TextEncoder().encode(`${day}:${site}:${ip}:${userAgent}`),
);
// 最初の16バイト、16進数エンコード
上記のツールと同じファミリーですが、信頼モデルを変更するアーキテクチャ上の違いが1つあります。それは実行される場所です。Marginにはサードパーティのイベントエンドポイントがありません。SDKはインジェストルートをアプリにマウントするため、ブラウザはあなたのドメインと通信し、ビジターハッシュはあなたのサーバー上で、あなたのインフラストラクチャのみが見るリクエストから計算されます。Marginに転送されるのは、切り詰められたダイジェストです。生のIPとユーザーエージェントはあなたのスタックから決して離れません。
browser ── POST /api/margin (your app)
│ ip + ua からビジターハッシュを導出
└──> Margin API (ダイジェストのみ、org key)
ホストされたスクリプトとエンドポイントのツールでは、「IPをハッシュして破棄します」というのは、そのサーバーが必然的に生のリクエストを見るため、信頼に頼る約束です。Marginのモデルでは、そのホップで信頼に頼るものは何もありません。ベンダーはそもそも識別入力に決してアクセスしません。
正直になるべき部分
Marginのハッシュ入力がPlausibleがランダムな日次ソルトを使用する場所で日付を使用していることに気づいたかもしれません。それは意図的なトレードオフであり、コストがかかります。ランダムなソルトは、ハッシュを再計算不可能にするために存在します。ソルトがない場合、ダイジェストを保持し、可能な入力を列挙できる人は誰でもメンバーシップテストを実行できます。
// 秘密の質問