Canvasフィンガープリンティングの一歩先へ:同一のChrome同士がなぜ異なるピクセルをレンダリングするのか
同じChrome、同じOS、同じGPUを持つ2台のデバイスでも、canvasのピクセルは異なってレンダリングされます。レンダリングパイプラインが非決定的である理由、プライバシー目的のノイズ注入が裏目に出る理由、そして現代の検知スタックにおけるcanvasの位置づけを解説します。
Canvasフィンガープリンティングは2012年から本番環境で使われてきました。考え方は単純です。ブラウザに2Dコンテンツをレンダリングさせ、ピクセルを読み出してハッシュ化します。同じ命令に対してデバイスごとにわずかに異なるピクセルが生成され、その差異はセッションをまたいでデバイスを識別できるほど安定しています。
ここまではよく知られています。あまり理解されていないのは、なぜこの技法が機能するのか——なぜ同一のハードウェア上の同一のソフトウェア構成が、それでも異なるピクセル出力を生成するのか、という点です。答えは、GPUドライバー、フォントレンダリング、浮動小数点演算の交差点にあります。
レンダリングパイプラインは決定的ではない
JavaScriptがcontext.fillText("Cwm fjord bank glyphs vext quiz", 4, 45)を呼び出すとき、Chrome自身がピクセルをレンダリングするわけではありません。Chromeは2DグラフィックスライブラリであるSkiaへの命令を生成し、Skiaは今度はGPUアクセラレーションされたバックエンド(通常はWindowsではANGLE、macOSではMetal、コンテナ内ではソフトウェアフォールバック)に描画コマンドを発行します。
各ステップで、実装上の小さな差異が積み重なります。
-
Skiaのフォントサブシステムがフォントファミリーを選択します。Arialがインストールされていなければフォールバックし、そのフォールバックはシステムにインストールされたフォントによって変わります。
-
選択されたフォントは、FreeType(Linux)、DirectWrite(Windows)、Core Text(macOS)を使って要求されたサイズでレンダリングされます。各ライブラリは異なるヒンティングアルゴリズムを持ちます。
-
サブピクセルポジショニングが適用されます。ここで、同一のフォントが異なるピクセルを生成し始めます。アルゴリズムは位置を小数ピクセルのオフセットに丸めますが、その丸めのルールはプラットフォーム間で異なります。
-
ラスタライズされたテキストがcanvasに合成されます。ブレンディングは、ハードウェアアクセラレーションされたシステムではGPUシェーダーによって、フォールバックモードではCPUコードによって行われます。
これらの各ステップは、1つか2つのピクセル値の差異をもたらしえます。個々には見えません。集合的には一意です。
GPUドライバーのバージョンがフィンガープリントを支配する
Intel UHD Graphics 620チップを搭載した2台のWindows 11マシンでも、GPUドライバーが異なればcanvas出力は異なりえます。Intelは年に数回ドライバーの更新を出荷しており、各更新はサブピクセルフィルタリング、ガンマ補正、テキストのアンチエイリアシングを変えることがあります。
これは、canvasフィンガープリントの安定性がドライバーの更新頻度によって上限を定められることを意味します。グラフィックスドライバーをアップグレードしたユーザー——多くの場合Windows Update経由でひそかに——は、canvasフィンガープリントが変わることになります。
検知システムは、canvasフィンガープリントを単独の識別子ではなく多くのシグナルの1つとして扱うことでこれに対処します。canvasが変わっても他のシグナル(TLSフィンガープリント、WebGLレンダラー、タイムゾーン、インストール済みフォント)が安定していれば、その訪問者は依然として認識されます。
絵文字のレンダリングは宝の山
canvasフィンガープリントで最も識別力の高い要素は、多くの場合テキストや図形ではなく絵文字です。絵文字のレンダリングは、OSに同梱される絵文字フォントに依存します。WindowsではSegoe UI Emoji、macOSではApple Color Emoji、AndroidではNoto Color Emojiです。
単一のOSの中でさえ、絵文字のレンダリングはバージョンをまたいで変化します。Windows 10の虹の絵文字のレンダリングはWindows 11のものと異なります。iOS 16のにっこり顔はiOS 17のものと異なるアンチエイリアシングを持ちます。
現代のcanvasフィンガープリンティングのスクリプトは、Unicodeに新しく追加されたものを含む絵文字の並びを意図的にレンダリングします。iPhoneのSafariを実行していると主張する訪問者が、iOS 17で追加された絵文字をレンダリングできないなら、それはより古いiOSを実行しているか——あるいはプラットフォームについて嘘をついているかです。
自動化フレームワークは特徴的なフィンガープリントを生成する
GPUのないDockerコンテナ内で実行されるヘッドレスChromeは、レンダリングにSwiftShaderを使用します。SwiftShaderは、内部的には一貫しているものの、ハードウェアアクセラレーションされたどのChromeとも異なるcanvas出力を生成します。
その署名は識別可能です。不自然なほどクリーンなアンチエイリアシング、グラデーション領域における特定の色値、そして既知のどのGPUドライバーとも一致しないテキストのエッジです。Linuxコンテナ内のChrome 124でSwiftShaderに一致するcanvasフィンガープリントは、ほぼ確実に自動化です。実在のユーザーがこのような形でブラウザを実行することはありません。
Puppeteer-extra-stealthは、toDataURL()をインターセプトして改変されたデータを返すことでcanvas出力を偽装しようとします。しかしその改変は、しばしばそれ自体のアーティファクト——セッションをまたいで繰り返されるノイズのパターン——を持ち込み、逆説的に、アンチフィンガープリンティングの試みを何もしないよりも検知されやすくします。
ノイズ注入がうまく機能しない理由
Braveのようなプライバシー重視のブラウザは、トラッキングを防ぐためにcanvas出力にノイズを注入します。ピクセル値に小さなランダムな変動を加えることで、同じデバイスがセッションごとに異なるフィンガープリントを生成するようにする、という考え方です。
実際には、ノイズ注入には3つの問題があります。
1. ノイズそのものがフィンガープリントです。読み込みのたびにcanvas出力が変わるのに、他のシグナルは同一のまま、という訪問者は、ノイズを注入するブラウザとして認識できます。それ自体が識別材料になります。
2. ノイズのアルゴリズムは安定しています。Braveの実装は、GPUレンダリングの自然な分散とは一致しない特定のノイズパターンを生成します。検知システムは、ノイズ注入されたように見えるcanvas出力と自然に見える出力を識別できます。
3. 高度なシステムはサンプルを組み合わせます。同一ページ上での、あるいはセッションをまたいだ複数のレンダリングは平均化できます。ノイズが小さければ、その平均は根底にある決定的なフィンガープリントへと収束します。
Canvasフィンガープリンティングが捉えるもの
明白な視覚的出力にとどまらず、canvasフィンガープリンティングは副次効果としていくつかのデバイス属性を抽出できます。
レンダリング時間 — canvasのレンダリングにかかる時間はGPUの能力を明らかにします。ディスクリートGPUを備えた4Kデスクトップは、ミドルレンジのノートパソコンより速くレンダリングします。
テキストメトリクス — measureText()は、フォントレンダリングによって変わる正確なピクセル幅を返します。テキスト幅は、レンダリングをせずともフィンガープリントとして使えます。
合成モードのサポート — 一部のブラウザやGPU構成は拡張された合成モードをサポートします。サポートの有無をテストすることで、能力の境界が明らかになります。
フィルター効果 — canvasに適用されるCSSスタイルのフィルターは、GPUごとに異なる出力を生成します。特にブラーとドロップシャドウのフィルターです。
検知スタックにおけるcanvasフィンガープリンティングの位置づけ
単独では、canvasフィンガープリンティングは堅牢な識別子ではありません。ドライバーは変わります。ユーザーはブラウザを切り替えます。プライバシー志向の利用者層ではノイズ注入が一般的です。
その真の強みはクロスチェックにあります。あるセッションがCookieやアカウントログインに基づいてリピーターであると主張するとき、canvasフィンガープリントは第2のシグナルとして機能します。過去のフィンガープリントと一致すればその主張を裏づけ、不一致であればアカウント乗っ取りやセッションハイジャックのフラグを立てます。
ボット検知においては、canvasフィンガープリンティングはコンテナ化された自動化の捕捉に優れています。実在のユーザーは実在のGPUを持ちます。実在のGPUは識別可能なレンダリング署名を生成します。SwiftShaderのように、あるいは下手に偽装されたcanvasのようにレンダリングするものはすべて、「ほぼ確実にボット」のバケットに入ります。
2012年の技法が2026年でも機能するのは、同じ理由によります。レンダリングは環境ごとに決定的でありながら、完全に偽装することはほぼ不可能です。隠そうとするあらゆる試みが、その試み自体の痕跡を残すのです。