
2026/07/13 6:12
Chromium 148 より*Math.tanh*は下位の OS を特定するフィンガープリントとして使用可能になった
RSS: https://news.ycombinator.com/rss
要約▶
日本語翻訳:
Chrome バージョン 148 以降、Chrome や同様のブラウザは特定の数学的な挙動を通じて正確なオペレーティングシステムの詳細を漏らし、高度なデジタルフィンガープリンティングを可能にしています。これはまず
Math.tanh 関数から始まり、bundled routines からホストライブラリへと切り替わったことで、macOS、Windows、Linux のユーザーが使用しているか否かに基づいて小数点以下最終桁が 1 ユニット異なる独自のプロファイル(OS シグネチャ)が作成され、他の V8 数学関数とは異なりプラットフォーム間で同一ではなくあります。また、すべての CSS 三角関数はホスト libm を直接呼び出すことで OS を漏らします。macOS Apple Silicon では、スカラーおよびベクトルフレームワーク間のアーキテクチャ固有の分断により計算に著しい乖離が生じます。Scrapfly はこれらの問題に対処するため、数学的ビットごとの再現を含め、Linux のなりすまし用の真正な Windows ライブラリのマッピングや特定の ARM/x86 FMA(fused-multiply-add)挙動の処理を含む特別ソリューションを開発しました。彼らの Scrapium ブラウザはこれらの OS シグネチャを真正的なシステムと完全に一致するようになりすまえ、同時に最適な速度のためにハードウェア FMA を能動的に有効化します(ネイティブエミュレーションより最大約 6 倍高速)。高度なマッピング技術および数十万もの入力を対象とした厳格な検証を通じて、ユーザーは微妙な数学的な丸めエラーや将来のアップデートにおけるアーキテクチャの癖に基づく識別を防止する同時期にネイティブレベルのパフォーマンスで完全なステルスを実現します。本文
指紋検出回避:OS 固有の数学ライブラリによる指紋解析と完全再現技術
指紋検出は通常、WebGL、フォント、オーディオなどの分野を対象にしますが、より静かに機能する信号として**数字の最後の数ビット(最低限の有効桁)**に宿る情報も存在します。以下にその仕組み、対策、および完全再現の技術詳細を解説します。
指紋検出の確認
Math.tanh の計算結果は、使用する数学ライブラリ(libm)によって異なります。以下のコマンドを実行すると、OS ごとに異なる値が出力されます。
Math.tanh(0.8)
結果の例:
- 本物の Linux Chrome (glibc 使用):
0.6640367702678491 - 本物の macOS Chrome (libsystem_m 使用):
0.664036770267849 - 本物の Windows Chrome (UCRT 使用):
0.6640367702678489
仕組みと注意点
この値は定数ではなく、オペレーティングシステム(OS)によって正確なビット構成が異なります。
- macOS: Apple の数学ライブラリ (
) を介して計算。libsystem_m - Linux:
を介して計算。glibc - Windows: ユニバーサル C ランタイム (UCRT) を介して計算。
3 つのライブラリは入力値の約 4 分の 1 の精度で合致しないが、通常は有効桁の最後の 1 ユニット(1 ULP)程度の差です。入力が
0.8 の場合、この微小なビット構成の差により 3 つの OS で異なる結果に収束します。
Chrome バージョンによる挙動の違い
Chrome 150 を使用した測定結果では、以下の通り動作します:
: Linux・macOS・Windows の 3 者とも一致(情報漏洩なし)。Math.tanh(0.5)
: Linux は一致するが、macOS と Windows では 1 ULP 差。Math.tanh(0.7)
: 3 つの OS で全て異なる結果となり、ばらつきは 2 ULP に及ぶ(識別に最適)。Math.tanh(0.8)
: Windows が他と比べて 1 ULP 差のみ。Math.tanh(0.9)
重要: Chrome 148 以降、V8 はホストの
から読み取るよう仕様変更されました。以前は bundled な fdlibm ポートを使用していたため、どの OS でも同じビットを返し情報漏洩していませんでした(V8 コミットlibmで変更)。c1486295ae5
なぜ 1 つの関数が異なるビットを返すのか
IEEE 754 は double の保存方法を定義しますが、
sin や cos、tanh、exp が正しく丸められることを強制していません。
正しく丸める計算は高コストであるため、各ベンダーは速度と精度のバランス(分数 ULP を犠牲にして)を取る独自の libm を提供しています。これらには自社固有のミニマックス係数やルックアップテーブルが組み込まれています。
主要なライブラリ一覧
- Linux: glibc
- macOS: Apple
libsystem_m - Windows: UCRT (
)ucrtbase.dll
これらはほぼすべての場所では一致しますが、OS を分類するのに十分な頻度で分岐します。検出器には数学的計算の知識が必要ではなく、単に結果テーブルを持つことで検出が可能です。
四つの罠:単純な再実装は失敗する
「Mac の関数を再実装する」というアプローチは、以下の 4 つの理由で接触するだけで失敗します。
1. 一部の数学機能だけが漏洩する
V8 は独自の数学を静的にリンクしています。
、Math.exp
、Math.pow
など:bundled な llvm-libc から提供(全 OS で同一)。Math.atan
/Math.sin
:bundled な glibc 由来の double ルーチンから提供(全 OS で同一)。Math.cos- 例外
:Chrome 148 以降、V8 はプラットフォーム固有のMath.tanh
を使用し、ホストの libm から読み取ります。これが唯一 OS を漏洩するstd::tanh
関数です。Math.*
2. JavaScript の数学と CSS の数学は別々のコードパス
CSS の
sin()、cos()、atan2() は、Math.sin と共有されるコードを持たず、以下のプロセスを経て計算されます:
- レイアウトエンジンで角度を度数に変換。
- プラットフォーム固有の
を呼び出し(値を減少させ)。std::sin
これは直接ラジアンで計算する
Math.sin と異なる結果となり、すべての 7 つの CSS 三角関数がホストの libm の情報を漏洩します。単なる関数ではなく、度数変換とラジアン〜度のステップをビット単位で再現する必要があります。
3. macOS は互いに不一致を生じる 2 つの数学ライブラリを持つ
Apple Silicon は以下の 2 つの異なるライブラリを持っています:
- スカラー版:
(使用例:libsystem_m
)。Math.tanh - ベクトル版: Accelerate フレームワーク (
,vvsin
)。vvtanh
これらのコードは異なり、100 万の入力において関数により10〜89% のケースで発散します。
- 例:
はスカラー版で正確にcos(0)
ですが、Accelerate では1.0
と異なります。0.9999999999999999
したがって、「Apple の数学を再現する」ことは定義が曖昧です。正しい解決策は、デバッグプロトコルで実際の Mac を駆動し、実際の Chrome から正確な double 値を読み取り、スカラー
libsystem_m と CSS/オーディオコンプレッサー用のスカラー版を後援することです。Accelerate(FFT 等)との間で間違ったライブラリを選ぶと、1 ULP ずれだけでなく深刻な問題になります。
4. アーキテクチャによる漏洩
ARM と x86 は、融合乗算加算(FMA)や NaN の符号伝播において異なります。コンパイラが一つのターゲットで FMA を融合させ、別のターゲットでは行わない場合、ドリフトが発生します。
マップ:どこで何かが漏洩しているか
| 操作 | V8 Math.* (JS) | CSS calc() | Web Audio |
|---|---|---|---|
, , | V8 bundled (漏洩なし) | ホスト libm (漏洩あり) | Accelerate / スカラー版 |
, , , | V8 bundled | ホスト libm (漏洩あり) | 使用しない |
| ホスト libm (漏洩あり) | なし | 使用しない |
, , | V8 bundled | ホスト libm (漏洩あり) | コンプレッサー / FFT |
- V8 bundled: 静的にリンクされており、すべての OS で同一です。
- ホスト libm: OS を漏洩するプラットフォーム固有のライブラリ(Mac:
, Linux: glibc, Windows: UCRT)。libsystem_m - Accelerate: Mac の Web オーディオ DSP に使用される Apple の vDSP。
3 つの重要な情報源:
- V8 はほぼすべての処理を bundled 数学でルーティングするため、JS は
のみに情報が漏洩します。Math.tanh - Blink はすべての三角関数に対してホストの libm を直接呼び出すため、CSS は常に情報源となります。
- Mac の Web オーディオは、FFT では Accelerate ですが、DynamicsCompressor(単位の超越関数)ではスカラー版
を使用します。1 つのオーディオグラフに 3 つ異なるライブラリが存在します。libsystem_m
WASM について
WASM は超越演算オペコードを持っていないため、数学は bundled libm から供給されます。その算術(
f64.sqrt, f64.mul)はハードウェアが行うため、WASM の数学はすべての OS で同一です。指紋軸は NaN の正規化と SIMD 丸めにおける ARM と x86 の分岐のみとなります。
これを閉じる方法:完全再現技術
単なる出力の乱す(ノイズなし)では無意味です。「主張している OS と同一である」ことが目標です。以下に 3 つの主要なアプローチがあります。
アプローチ A: アルゴリズムの完全再現
目標の libm からミニマックス係数などを回復し、移植可能な C に転記します。良い
tanh を構築するのではなく、「彼らのtanh」を構築します。
Apple の sin 多項式(libsystem_m から抜き取った係数)を例示します:
// Apple が発火する各融合乗算加算 (fma) は明示的な fma() として記述されます。 // 係数のビットパターンはそのままコピーされ、小数点表記では異なる丸めになります。 static const double P[6] = { 0x1.5d8fd1fd19ccd * p-33, -0x1.ae5e5a9291f5d * p-26, 0x1.71de3567d48a1 * p-19, -0x1.a01a019bfdf03 * p-13, 0x1.111111110f7d0 * p-7, -0x1.5555555555548 * p-3, }; static double sin_poly(double x2) { double p = fma(x2, P[0], P[1]); p = fma(x2, p, P[2]); p = fma(x2, p, P[3]); p = fma(x2, p, P[4]); p = fma(x2, p, P[5]); return x2 * p; // 呼び出し側で完了: sin(x) = fma(x, x2*p, x) }
決定論的な再現:
- 明示的な
が重要です。fma() - コンパイル時に FMA 収縮をオフにする (
)。これにより、コンパイラが自らの融合を発明したり捨てたりすることを防ぎ、Apple と同じ演算フローになります。-ffp-contract=off - 結果は FMA と非 FMA CPU で同一に、ARM マシンと x86 フリート上で同一になります。
アプローチ B: オリジナルライブラリの直接読み込み
再現が割に合わない場合は、オリジナルの libm をマップします。
- Windows UCRT:
を実行時にメモリ上にマップし、エクスポートを直接呼び出します。コードが本物なので、リバースエンジニアリングなしでビットは本物です。ucrtbase.dll
// Windows x64 ABI (System V ではない)。ms_abi がないとクラッシュします。 typedef double(__attribute__((ms_abi)) * D1)(double); // tanh, sin, ... typedef double(__attribute__((ms_abi)) * D2)(double, double); // atan2 // マップされた DLL コードは CFI リージスターズでないため、 // -fsanitize=cfi-icall (生産環境オン) は #UD-trap を発生させます。 [[clang::no_sanitize("cfi-icall")]] double ucrt_tanh(double x) { return ucrt.loaded ? ucrt.tanh(x) : std::tanh(x); }
詳細な調整:
- UCRT の数学関数は
で始まって、スカラーか FMA/AVX2 パスかを選択する CPU ディスパッチフラグを読み取ります。mov eax, [rip+disp32] - 新鮮なマップではこのフラグが 0 になり遅いパスを取得するため、初期呼び出し前にFMA パスに強制することで、実際の Windows マシンとビット単位で一致します。
アプローチ C: フックによるゲート制御
エンジンが libm を呼ぶ単一の関数をフックし、主張する OS にゲートします(Linux は glibc を保持し、Mac では再現版を使用)。
速度最適化 (FMA 有効化): 完全に再現されていても低速であれば情報源となります。ソフトウェア呼び出しの
fma() はハードウェア FMA より 2.5〜6 倍遅いです。
- デフォルトの x86 ベースラインはハードウェア FMA 以前の挙動のため、比率が不自然です。
- ハードウェア FMA をオンにすると、各融合演算は 1 インストラクションになり約 6 倍高速化し、glibc よりも速く、ビット単位で同一になります。
バリデーション
出荷前の証明として、以下のテストを実行します:
- 871,000 の入力実行: 稠密グリッド、間隔境界、サブノーマル、符号付きゼロ、無限大、NaN など。
- 本物のデバイスオラクル: スカラーと Accelerate の両方の結果を計算する実際の Mac で不一致箇所を検出。
- 本物のブラウザアンカー: 実際の Mac Chrome (
と CSS 三角関数) を完全精度で計算し、指紋検出器の基準とする。Math.tanh
確認事項:
と CSS のMath.tanh
/sin
/cos
/tan
/asin
/acos
/atan
について、再現版は出荷バイナリ内の機械コードと完全に一致しています(Bit-for-bit パラリティ)。atan2- ドメインエッジもチェックされます:例えば実際の Mac では
は 0 に解決(NaN クランプ)しますが、ナイーブな再現版では 90 度となりません。asin(2)
結論:なぜそれが重要なのか
数学は決定論的であり、プローブするのは簡単ですが、偽装するのは困難であり、スプーフィングスタックのレーダーにはほぼありません。
- 防衛者にとって: 強いシグナルとなります。
- スクレイパーにとって: 負債となります。
これを正しく行うには、ベンダーの libm 内部をリバースエンジニアリングし、3 つのエンジンが呼び出しサイトごとに数学をルーティングする方法をマッピングし、アルゴリズムを最後のビットまで一致させ、アーキテクチャを超えて決定論性を維持し、実在するハードウェアに対して証明する必要があります。
これにより、API を介して macOS として提示するとしても、アイデンティティはコサインの丸めまで保持され、実在するトラフィックと区別できなくなります(Scrapfly / Scrapium の技術スタック)。