created_at
updated_at
tags
toc
headings

牧歌的 Cookie の終焉

Intro

Cookie は、ブラウザに一度保存すれば、次からその値を自動的に送ってくるという、非常に都合の良い仕様から始まった。

State Less が基本だった Web にセッションの概念をもたらし、今ではこれが無ければ実現できないユースケースの方が多い。

冷静に考えればふざけてるとして思えないヘッダ名からもわかるように、当初はこのヘッダがこんなに重宝され、 Web のあり方を変えるかもしれないくらい重要な議論を巻き起こすことになるとは、最初の実装者も思ってなかっただろう。

そんな Cookie が今どう使われ、 3rd Party Cookie (3rdPC) の何が問題になっているのかを踏まえ、これからどうなっていくのかについて考える。

Web にある API の中でも Cookie はいくつかの点で特異な挙動をする

  • 一度保存すれば、次から自動で送る
  • 現在の Origin と関係なくどこにでも送る (CORS 不要)
  • 長さ以外特に制限が無く任意のシリアライズされた値を入れられる

結果、その緩い仕様がゆえに、 Cookie はあらゆるユースケースを背負い込むことになった。

設定値保存

例えば、テーマカラーを設定で変えられるようなサービスがあった際に、その情報を Cookie で持つこともできる。

Set-Cookie: theme=dark
Cookie: theme=dark

これにより、同じユーザでも違うブラウザであれば設定値を変えることができ、 JS を使わなくてもサービス側が CSS を切り替えてページを返すといった実装が可能だ。

毎回リクエストに乗るということを無視して JS API を用いれば、単なる KVS としても使えるため、 LocalStorage 以前は Cookie がその代替を担っていた。

セッションの維持

Cookie Store の中身はユーザが簡単に変更できるため、サーバからランダムな値を付与し、その値が送られてくることでユーザの連続したリクエストを紐付けるようになった。これがセッションの維持だ。

多くのサイトで、初回のアクセス時にとりあえず付与されているだろう。

Cookie: SID=q1w2e3r4t5y
Cookie: SID=q1w2e3r4t5y # 同じユーザであることがわかる

Credential

単に個々のアノニマスユーザを識別するだけだった SID に対して、サーバ側でユーザアカウントを紐付ける行為がログイン認証だと言える。

メールサービスが受信ボックスを見せるのも、 EC サイトが購入履歴を見せるのも、全てこの乱数がキーとなり、もしこのキーを盗み出せれば、そのユーザになりすますことができる。

一部の仕様の中で、 Cookie のことを Credential と呼ぶのはこのためだ。

Web における攻撃の大半は、最終的にこの Cookie を盗む(ex. XSS, MITM etc)か、 Cookie の挙動を利用(ex, CSRF, Timing Attack etc)している。

HTTPS Everywhere が必要なことも、 HttpOnly や Secure 属性が必要なのも、 Credential としての Cookie が漏れることを防ぐために必要不可欠だからだ。

SSO

実装は色々あるが典型例としては、あるサイト a.example.com と b.example.jp で Single Sign On を実現したい場合、共通の認証基盤として auth.example を用意する。全て違うドメインだ。

a.example.com にアクセスした場合、一度 auth.example にリダイレクトすることで、ログインフォームでログインさせ Cookie を付与する。さらにワンタイムトークンなどをクエリストリングに付けてリダイレクトバックしてやれば、 a.example.com は auth.example に裏で確認することでログイン済みとみなすことができる。

# auth.example からのレスポンス
HTTP/1.1 307 Temporary Redirect
# auth.example でログイン済みであることを示す Cookie
Set-Cookie: SID=q1w2e3r4t5
# このトークンを受け取った a.example.com は auth.example に裏で問い合わせる
Location: http://a.example.com?token=q1w2e3r4t5y6u7i8o9p0

次にユーザが b.example.jp にアクセスした際、 b.example.jp は auth.example にリダイレクトする。

auth.example には Cookie が飛ぶため認証済みであることがわかり、そのまま Token を付与した URL で b.example.jp にリダイレクトバックすれば、 b.example.jp はトークンを auth.example に問い合わせるだけで認証を完了できる。

ユーザからみれば、画面がぱぱっと何回か変わるだけで、フォームに入力する手間は省けるのだ。

これは、リダイレクトする際にも、 auth.example への Cookie が勝手に送られることを利用している。

そして、 a.example.com や b.example.jp にアクセスしたユーザにとっては、アクセスしたいサービスが 1st Party 自分を 2nd Party として、 auth.example は 3rd Party となるため、送られている Cookie は 3rd Party Cookie と呼ばれる。

Analytics

サイトに簡単なスクリプトを設置するだけで、単なるアクセスログだけでは取りにくいユーザの行動などの細かな情報を取得する仕組みが Analytics だ。

これも実装が色々あるが、よくあるのは <img> を使う方法だろう。

a.example.com のアクセスを analytics.example というアナリティクスサービスで収集したい場合、 a.example.com に以下のような <img> を埋め込む。

<body>
メインコンテンツ
<img width=1 height=1 src=https://analytics.example?id=q1w2e3r4>
</body>

<img> を使うのは、 CORS に違反せずに別 Origin へとリクエストを投げる簡単な方法だからだろう。

リクエストが飛べば、それだけでかなりの情報がわかる。

# そもそも IP からおおよその場所がわかる
# クエリに任意の情報をつければ送れる
GET /?id=q1w2e3r4 HTTP/1.1
# リファラで見ていたサイトもわかる
Referer: https://a.example.com
# どの言語圏の人かだいたいわかる
Accept-Language: ja,en-US;q=0.9,en;q=0.8
# 以下を見れば閲覧環境もそれなりに絞れる
Accept: Image/webp,image/apng,image/*,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
User-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.106 Safari/537.36

ここでさらに analytics.example から Cookie を付与すれば、このアナリティクスサービスを埋め込んだ全てのサービスで、ユーザがどう動いているかを追跡することもできる。

広告

あるユーザが、 a.example.com で見た商品に関するリコメンドを、 b.example.jp で表示したい場合も Cookie が応用できる。

これも実装が色々あるが、例えば ad.example という広告配信サービスを用意し、それを双方が iframe で埋め込んでいたとしよう。

a.example.com でユーザは商品ページを見ている。

<main>
PC 周辺機器
</main>
<aside>
   <iframe src=https://ad.example?page=pc-accessory></iframe>
</aside>

このとき、 ad.example には、ページの持ち主が設定したページの情報がクエリによって付与され、レスポンスで ad.example の Cookie が付与される。

# PC 周辺機器のページを見ていたことが伝わる
GET /?page=pc-accessory HTTP/1.1
Host: ad.example
HTTP/1.1 200 OK
Set-Cookie: SID=q1w2e3r4t5 # この Cookie で追跡
Content-Type: text/html
Content-Length: 1024

<html>
とりあえずの広告
</html>

次にユーザは全く関係ない b.example.jp にアクセスし、同じく ad.example の広告が埋め込まれていたとする。

<main>
SNS 的な何か
</main>
<aside>
   <iframe src=https://ad.example></iframe>
</aside>

このとき ad.example を取得するために送られるリクエストは以下のようになる。

GET / HTTP/1.1
Host: ad.example
Cookie: SID=q1w2e3r4t5 # さっき PC 周辺機器を見ていた人だとわかる
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1024

<html>
PC 周辺機器の広告
</html>

これも、 iframe であれ Cookie が送受信されることを利用している。

もちろん、 ad.example への Cookie は 3rdPC だ。

問題の本質

Cookie は、単純に「保存した値を自動で送信する」という仕様のままそこにあり、使われている。

問題はその仕様なのか、ユースケースなのかを考えれば、本来は後者だろう。

昨今の議論の中で「3rd Party Cookie」が問題として取り扱われるが、 SSO は良くてターゲティング広告はだめなのであれば、本当の問題は Cookie そのものではなく、その後ろにある「トラッキング」というユースケースなはずだ。

すると Cookie の挙動を変えるのではなく、そのユースケースを制限すれば良い。それが、かつて提案された DNT (Do Not Track) や P3P (Platform for Privacy Preferences) だった。

性善説で考えれば、そうしたヘッダを受け取ったサービスは、その内容を遵守してトラッキングを外すといったことを行えば、問題は全て解決した。しかし、現状この仕様の存在すら知らない人が大半なように、全く遵守されず、全く普及しなかった。あまつさえ、 DNT を送ることが Fingerprinting のベクタになるとして DNT の送信をやめたブラウザもある。

Cookie という挙動は維持したいが、そのユースケースの一部には問題がある、という現状を解決するための手段として、ユースケースの方を制限するという手法は、全く上手くいかなかった。

そうなると挙動に手を入れるしかない。結果として 3rdPC を「どう使われているかにかかわらず全てブロックする」ということになる。それが今起こっていることだ。例え互換性を壊したとしてもだ。

Web が互換性を壊すとき

「Web は互換性を重視する」という前提をよく聞くだろう。実際 Smoosh Gate のようなことが起こってしまう程度に、仕様策定の現場では互換性は重要視されている。

世の開発者がブラウザ互換性に悩まされる原因は、まだ標準化が終わってない仕様にモダンだと勘違いして飛びついた時か、逆に仕様に準拠できてない古いブラウザの挙動に依存したコンテンツを作り、ブラウザ側が改善された時が大半だろう。一度作業を終え、正しく実装されたあとの仕様は、互換を壊して変わることは基本ない。しかし、「基本はない」だけで例外はいくつかある。最もわかりやすいのは「ユーザの安全」だ。

Web はユーザの安全を守るために、互換性を壊すことがある。

この安全の実態には「セキュリティ」がまず上がる、 Spectre 対策で行われた Shared Array Buffer の無効化などが良い例だろう。その機能があることでユーザが危険に晒されるなら、機能を無効にしたり、挙動を変えたりといったことは、互換を壊してでも行われる。 AppCache のように Deprecate される機能の中にも、そういったものが少なくない。

そして、この安全というカテゴリに「セキュリティ」だけでなく「プライバシー」も入ってきているという流れが、昨今の議論の前提背景としてあると筆者は考える。

例えば、 3rdPC でのトラッキングは、一般的に「攻撃」とは見なされていなかった(でなければ世の中の大抵のサイトは有害サイトになる)。しかし、過去数年の特に個人情報漏洩や売買などに関わるインシデントがあとを経たず、一方でターゲティング広告がうざいという消費者の意見などから、徐々に「プライバシー」に対する考えが変わりはじめ、とうとうトラッキングにもメスが入るようになり、責任の所在はその時たまたまトラッキングの実装に使われていた 3rd Party Cookie に向かうことになった。

Tracking Prevention

Cookie の挙動を制限すると言っても、どの Cookie がどういったユースケースを担っているかなど、サービス提供者にしかわからない。

しかし、 1stPC は大抵問題ないが、 3rdPC は何らかの「意図しない何か」をしているという前提から、 3rdPC をブロックするという方法は、かなり前から知られていた。

特に Firefox や Opera はそうした設定を持ち、有効にしている人も少なくないとされ、 Privacy 意識の高いユーザはそうしたブラウザを好んで使うという雰囲気があったように筆者は思う。

これがあくまでもユーザの Opt-In なのは、サイトが崩れた場合などのために、オフにできないと困るという性質が少なからずあったためだろう。

ところが Opt-In のままでは大半のユーザは無効のままなので、本来は「できるだけサイトを壊さないように、デフォルトで有効にすべき」という話になる。

そこで、「その Cookie が何のユースケースで使われているかわからないなら、機械学習で推測して、トラッキングに使われていそうなやつだけブロックすれば、より正確にユーザを守ることができるのでは」という発想で実装されたのが Safari の ITP(Intelligent Tracking Prevention) だ。

実際にどういった機械学習が行われているかはブラックボックスであり、そのために Single Sign On が壊れたりといったこともあるが、ある意味「ユースケースをターゲットにしてブロックする」仕様であると言える。

これに追従するようにして、 Firefox は Enhanced Tracking Prevention を、 new Edge は、 Tracking Prevention がデフォルトで有効になっており、それぞれやり方は多少違うかもしれないが、 3rdPC がブロックされている。 ITP のような機械学習というよりは、ブロックリストなどを中心とした実装になっているようだ。

Chrome は 2 年後までに 3rdPC を無くしていくことをアナウンスしており、現状ではまず SameSite=Lax をデフォルトにすることで、 opt-in されたサービス以外は自動で 3rdPC が飛ぶことを制限している。

いずれにせよ、 3rdPC から脱していくことは、ほぼすべてのブラウザで合意が取れており、こうした方法を使うと、結局 3rdPC 自体が、それがどういう使われ方をしているかに関わらずブロックされる。

多少壊れるサイトがあれば、それは 3rdPC 以外の方法で修正するなどを行ったとして、トラッキングについてはだいたい防ぐことができるかもしれない。

ただ、 3rdPC をブロックしただけで本当に問題は解決したと言えるのだろうか?

3rdPC Block という対処療法

特に広告のためのトラッキングというユースケースは、それが実現できることが重要であり、 3rdPC で実装されている必要があるわけではない

つまり、 3rdPC がダメなら、広告会社は別の実装方法を探すだけだ。それが Finger Printing や CNAME Cloaking であり、どちらもまたいたちごっこのように塞がれていくだろう。

Web を見捨ててアプリにプラットフォームを移しても、今は IDFA などを用いたトラッキングがまだ可能かもしれないが、そこでも同じような議論がまたおこるだけだろう。

結局、インターネットユーザが「トラッキングされたくない」という問題は解決するためには、トラッキングというユースケース自体について向き合う必要がある。

対処療法として 3rdPC をブロックしても、それは 3rdPC をブロックしただけ で、トラッキングの手法が別の何かに移っていくだけだ。ユーザから見たら問題は半分しか解決していない。

Privacy Sandbox

結局ユースケースの方が解決しなければ、どの仕様を塞いでも迂回され続けていく。そしてそういった仕様へのパッチは、通常の Web の利用にも影響が出てしまう可能性がある。

であれば、 ユースケースそのものを仕様化し、そこに誘導した方がよっぽどヘルシーだという発想が、 Privacy Sandbox のアイデアだと筆者は捉えている。

例えば、サイトを跨いでコンバージョンを取りたいなら、「コンバージョンを取るための API」を、ターゲティングをしたいなら「ターゲティングをするための API」を定義する。

これは、無尽蔵に取ることができる API を定義するという意味ではない。むしろその逆だ。

仕様定義の中では、プライバシーに配慮したエントロピーのコントロールや、ユーザの許諾を取るような権限モデルを考慮することになる。

またブラウザの API として実装することで、ブラウザ設定から Opt-In/Out を選択できたり、より自分に合った設定を拡張で導入したりといった選択をユーザが可能になる。

結果、ブロックするかしないかという All or Nothing な現状から、ユーザとサービス側に API を通じた確かな合意形成と、標準仕様とブラウザ実装によって担保されたプライバシーの保護機構が確立し、新たなエコシステムが生まれる。

実際そうなるかは別として、単に「ブロックする」ことしかしてない各 Tracking Prevention に対して、「じゃあどうすれば良いのか」を模索する動きが Privacy Sandbox だと言えるだろう。

これは Chrome が中心となって行っていることではあるが、その中心である Conversion Management API は、先行して Safari が提案した Ad Click Attribution とかなり類似している。 Storage Access API 含め、 Safari は ITP を始めたことと並行して、 Privacy Sandbox の礎となるような作業を既に始めていたと言ってもいいだろう。

いずれにせよ、まだ議論が始まったばかりで、中身はほとんど揃ってないが、この動きがどうなるか、各 API がどこまで実現するかは、今後の Web の生態系に大きな影響を及ぼすと筆者は考える。

よく、特定のベンダが 3rdPC を殺そうとしているとか、あのベンダは Web に興味がないから広告を締め付けることでアプリに誘導しているといった短絡的な解説が上がるのを見る。

そもそもベンダを主語に語れるほど問題は単純ではない。ほぼ全てのベンダが実際に 3rd Party Cookie をなんらかの形で利用し、それとは別の文脈で「プライバシー」を真剣に考えているチームがある。

そうしたチームがユーザを守るために 3rdPC をブロックしたことを考えれば、実際に 3rd Party Cookie を殺したのは、その緩い仕様に甘んじてトラッキングなどに利用し続けた、どちらかといえば Web サービス開発者側に責任が有ると言える。

そしてブラウザベンダにおいて、 3rdPC を脱していくことはこれからの Web を形作る上での合意事項となりつつあり、標準化や実装の現場は、既にその後の話をしている。

サービス開発者も、 「3rdPC が防がれたから別の精度の高い Tracking 方法を探す」のではなく、プライバシーの側面から自分たちが行うべきことを考え、必要となるユースケースに関しては Privacy Sandbox や各 API に対してフィードバックしていく方がよっぽど健全だろう。

Web におけるマネタイズの大半は広告なのが現状だ。特に代替としてのマイニングなどが出遅れた日本にとって、広告を無くすことはできない。しかし、広告がこのままで良いとはならない以上、ならどうしていけば良いのかを考えるしか無い。

大手は、 Top Level Domain を SuperApp 的な構成にして、配下に並べたコンテンツ間を 1st Party Cookie でトラッキングするといった荒業もできるかもしれないが、そうではない小さなドメインにも価値があるものは多くあり、それらが広告によって成り立っていることは十分考えられる。(このドメインは こういう理由 で広告を貼っているだけなので、特に困りはしないが)

そしてそもそも、巷にはトラッキングが無いと本当に広告の収益が下がるという報告もあれば、コンテキストの方が重要でターゲティングが無くても広告の価値は下がらないと報告も有る。つまり、自分の持つサービスにおいてどうなのかを計測せずに 3rdPC Block の影響を十把一絡げに語ること自体も怪しい。

自分の持っているサービスと、そこで使われている 3rdPC と、その有無による影響の正確な数値をきちんと把握もせずに、「3rd Party Cookie が無いと広告はヤバイ」などと雑に捉えていては、本当に必要な議論を始めることすらできないのでは無いだろうか。

ゆるふわな仕様がゆえに、あらゆるユースケースを背負わされた Cookie は、 3rdPC Block の流れとともにトラッキングベクタの汚名を返上すると、 1stPC に集中することができるようになるだろう。

LocalStorage がある今、 Cookie を JS のための KVS として使う場面も減りつつあり、基本的には Credential としての Session ID を保持することに注力できる。

また、 Web には Origin をまたぐ際に明示的な許可を必要とするセキュリティモデル、 Same Origin Policy があるが、 Cookie はその以前から存在し、逸脱した存在だったことも問題の一つだ。

セッションの維持だけなら Same Origin で良く、それを Cookie 本来の仕様に合わせた概念が Same Site であり、既に Chrome はそれをデフォルトにし始めている。

それらを総合すると、セッションの維持が目的であれば HttpOnly かつ Secure__Host にロックされ SameSite=Lax に制限されたものが求められる。(そして HTTP State Token に繋がるかもしれない)。

Set-Cookie: __Host-SID=q1w2e3r4t5; HttpOnly; Secure; Path=/; SameSite=Lax;

結果として、「なんでもできた牧歌的な Cookie」は終わり、 Credential としてのユースケースを実現したこの姿に収束しつつ、他のユースケースは徐々に Cookie を離れて、各々の API を探していくことになりそうだ。

この始まりの先に保証はない、結果としてみんなが各ベンダプラットフォームにロックインされてでもアプリに移ることを選べば、「完成した安全な仕様」と「誰も使わない Web」が残る可能性も有るだろう。

今の Cookie の挙動にしがみつき、その変更を誰かのせいにして嘆いている暇があったら、自身が今持つユースケースを見直し、それが正しく実現される Post 牧歌的 Cookie 時代の Web の姿を考える方が、建設的ではないだろうか。

Jxck