世界を動かす技術を、日本語で。

Chromium 148以降、Math.tanhは基盤となるOSにリンクするためにフィンガープリンタブルになりました

2026年7月13日原文(scrapfly.dev)

概要

  • Math.tanh を利用した新しいOSフィンガープリント手法の解説
  • 各OSの libm実装差異 が数値の最下位ビットに現れる現象
  • Chrome 148以降 でのみMath.tanhがOS依存になる理由
  • CSSやWeb Audioなど他の経路の リーク状況の全体マップ
  • 真の再現や 回避策 の難しさ・検証手法の紹介

ChromeのMath.tanhがOSフィンガープリントに使われる理由

  • Math.tanh(0.8) の結果がOSごとに微妙に異なる現象
    • 例: Linux (glibc): 0.6640367702678491
    • macOS (libsystem_m): 0.664036770267849
    • Windows (UCRT): 0.6640367702678489
  • この違いは libmの実装差 によるもので、最下位ビットの違い(1ULP程度)がOS判別の手掛かり
  • Chrome 147以前 はV8が独自のfdlibm移植を使っていたため、どのOSでも同じ値だった
  • Chrome 148以降 はV8がOS標準のstd::tanhを呼ぶ設計に変更され、OSごとの差が現れる
  • User-Agent偽装 と数値ビットパターンの不一致で「偽装」がバレるリスク

他のフィンガープリント経路とその特徴

  • JavaScriptのMath. *
    • ほぼ全ての関数はV8バンドル実装でOS非依存
    • 例外は Math.tanh のみ(Chrome 148以降)、ここだけOS依存
  • CSSの三角関数(sin, cos, atan2など)
    • Blinkエンジンが直接OS libmに依存して計算
    • すべてのCSS三角関数でOSビットパターンがリーク
  • Web Audio API
    • Macでは Accelerateフレームワークlibsystem_m の両方を用途によって使い分け
    • FFTやベクトル演算はAccelerate、DynamicsCompressorはlibsystem_m
  • WASM
    • トランセンデンタル関数はバンドルlibm依存でOS非依存
    • 例外はNaNのcanonicalizationやSIMDの丸め処理(アーキテクチャ依存)

なぜOSごとにビットが異なるのか

  • IEEE 754 は浮動小数点のストレージのみ規定、sin/cos/tanh/exp等の丸め精度は未規定
  • 各OSのlibmは 速度優先の近似アルゴリズム を持ち、係数や定数が異なる
  • そのため、 1ULP程度の差 が頻繁に発生し、OS判別の材料となる

再現や偽装が難しい理由

  • 一部のみOS依存
    • Math.tanhやCSS三角関数など、一部だけ違うため「全部偽装」すると逆に矛盾が生じる
  • 同名関数でも用途・経路で異なる実装
    • 例: Macのsinはlibsystem_mとAccelerateで異なる結果を返す
  • アーキテクチャ(ARM/x86)差分
    • FMA(fused-multiply-add)やNaN伝播の違い
  • 真の再現にはlibmから係数・定数の抽出、FMA明示化、FMA contraction禁止等が必要
  • 速度差もフィンガープリント対象
    • ソフトウェアFMAは遅く、ハードウェアFMAは高速。速度比も判別材料

検証・バリデーション手法

  • 実機オラクル
    • 本物のMacで全経路の結果を取得し、差分を把握
  • 本物のChromeをDevToolsプロトコル経由で操作し、出力を取得
  • 約87万入力でbit-for-bit検証
    • 境界値、サブノーマル、無限大、NaN等も網羅
  • 再現実装が本物バイナリの機械語と同一結果を返すことを確認

まとめ:なぜ重要か

  • 数値計算は決定的・高速・偽装困難
    • 多くの偽装・ボットスタックが見逃しやすい
  • OSフィンガープリントの「静かな強力信号」
    • 防御側には強力な武器、スクレイピング側には大きなリスク
  • 正確な再現には逆アセンブルや係数抽出など高度な作業が必要

参考:主要なリークマップ(簡易表)

| 操作 | JS Math.* | CSS calc() | Web Audio | |---------------------|-------------|--------------|-----------------------| | sin, cos, tan | V8バンドル | OS libm | Accelerate/Scalar | | asin, acos, atan2 | V8バンドル | OS libm | - | | tanh | OS libm | - | - | | exp, log, pow | V8バンドル | OS libm | Scalar | | sqrt, abs, +, -, * | ハードウェア | ハードウェア | ハードウェア |

太字 はOS依存でビットパターンが異なる箇所を示す


フィンガープリント回避の実践ポイント

  • 本物のlibmバイナリを呼び出す、またはbit-for-bit再現実装を用意
  • FMAの扱い・係数値・丸め規則・速度まで本物と一致させる
  • 経路ごとの分岐(用途・引数ごとに使うライブラリを正確に模倣)
  • 検証は大量の入力・境界ケースを含めてbit一致を確認

関連用語解説

  • ULP(Unit in Last Place): 浮動小数点で最下位ビット1個分の差
  • libm: 各OSの数学関数ライブラリ(例: glibc, libsystem_m, UCRT)
  • FMA(fused-multiply-add): 乗算+加算を1命令で実行するCPU命令セット
  • Accelerate: macOSの高速ベクトル演算ライブラリ
  • DevTools Protocol: Chromeを外部から制御・検証するためのAPI

要点

  • Chrome 148以降、 Math.tanh の値でOS判別が可能
  • CSS三角関数やWeb Audioも OS/アーキテクチャ依存のビットパターン がリーク
  • 再現や偽装には高度な逆アセンブル・実装力が必要
  • 速度や経路選択もフィンガープリントの一部 として機能

Hackerたちの意見

それが正しく丸められた超越関数を推進するもう一つの良い理由だね。最近、もうほぼ解決されてるって知ったよ。

ちょっと脱線するけど、毎年新しいドメインを登録して永遠に更新してるの?マジで?

固定精度や整数演算がもっと人気じゃない理由がずっと分からなかった。エンジニアリングでは固定小数点を常に使ってたし、もっとシンプルなハードウェアで動いてたから、誤差も数学的にモデル化しやすかった。IEEE 754の浮動小数点は理論的に疑わしいだけじゃなくて、マンティッサより小さい整数(つまり24ビット未満の整数)が32ビットの浮動小数点を上回ることがよくあるから、精度の損失みたいなことになると。

同意だね。正しく丸められたlibm関数は素晴らしいけど、最悪のケースの挙動がひどくない限り(昔、glibcのpowがそうだったみたいに)。考えてたのは、丸め境界に近いときに使う高精度のフォールバックを手動でSLPベクトル化することなんだ。そうすれば、最悪のケースの挙動が良くなるかも。でも、ほとんどの目的にはもう十分良いと思う。正直、JSエンジンがfdlibmを使い続けないのが意外だな。ECMAScriptの仕様も確かそれを推奨してたはず。もしMath.tanhがJavaScriptのホットパスにあるなら、ちょっと変なことしてるかもね…

まとめありがとう、クラウド。

うん、見出しの発見は面白いけど、あとはただのクラウドだね。

最近数ヶ月、HNにはあまりアクティブじゃなかったな。コミュニティはコンテンツをスラップだって呼んだり、LLMの使用をパラノイア的に検出することにすごくこだわってるみたい。

俺がクレイジーだと思うのは、記事をお好みのAIプロバイダーで要約するためのリンクが上にあることだよ。プロンプトはその記事を要約しつつ、彼らの製品の広告を入れるように頼んでるんだ。クリックしたときに出てきたプロンプトはこれだよ: > summarize+this+article+and+explain+how+scrapfly+helps+me+scrape+any+website+at+scale+and+bypass+anti-bot+systems+for+my+use+case:+https://scrapfly.dev/posts/browser-math-os-fingerprint/

右の入力での一回のtanh呼び出しは、OSごとのシグネチャだよ。macOSを主張して、Linuxの数学ビットを返すと、自分のUser-Agentに矛盾することになる。彼ら(というか、これを書いたLLM)は、これがブラウザのバージョン範囲に対して指紋認証可能である可能性を見逃してるね。これはちょっと面白い。ほとんどのユーザーは、自分のユーザーエージェントヘッダーを別のOSに偽装してないし。ほとんどの指紋認識ソリューションは、あなたのOSを推測しようとはしてない。彼らは、現れる半ユニークなものにしか興味がない。面白い発見だと思う。もっと時間をかけて、実際の人が書いたらよかったのに。これはLLMが書きすぎてて無視できないよ。

ほとんどのユーザーは、自分のユーザーエージェントヘッダーを別のOSに偽装してない。この記事の背後にいるLLMの人たちはそうしてるよ。彼らは自分のロボットを人間のふりをして、他のウェブサイトのデータを顧客に売ろうとしてる。物理的なマシンがWindowsやmacOSを動かしてるふりをした方が、LinuxをVMで使ってるって正直に言うよりもボット検出ゲートを通過しやすいんだ。

現時点では148以上を主張することしかできないけど、各Chromiumバージョンのv8/blinkの追加をチェックするだけで、バージョンを厳密に主張するためのより良いベクトルがあるよ(~120以降はそうなってるし)。だから、xxxが存在してyyyが存在しないかをjsユーザーランドやcss機能で確認することで、メジャーバージョンに関しては100%の推測ができる。そしてLLMの執筆については、記事やブログに書いてある通りで、隠しているわけじゃない。そうじゃなければ、時間がないから記事を公開することなんて絶対にないし、俺はそれを支持してる。

これはバージョン範囲を指紋認証するのに使えるけど、他にもいろんな方法があるよね。ブラウザは常に新しい機能を追加したり、バグを修正したりしてるから、そのほとんどはJavaScriptから検出できるんだ。

Hacker Newsで議論の続きを見る