概要
先日ふと自分のPCのフィンガープリントを取ってみたところ「IPアドレス」など様々な項目が並ぶ中に「Math.tan」という変な項目を見つけました。
「なぜ三角関数が出てくるの?」と気になって調べてみたところ、**三角関数の値はブラウザやOSの実装により微妙に異なることがあり、特定の式をブラウザに計算させることで利用者を識別する手段になり得る1**という話でした。
面白そうだなと思ったので、本記事ではその手法で実際どの程度までブラウザ/OSを判別できるのか調査してみました。
検証方法
今回は様々な文献12の情報を参考に、以下の式を各OSの各ブラウザに計算させました。
tan(-1e300)
-
cosh(10)
(厳密には三角関数の類似ですが)
これら以外も10数種類ほど試したのですが、判別に使えたのはこの2つのみでした。
試したOSとバージョン
- macOS Catalina (ver.10.15.6)
- Windows 10 (OSビルド 19041.804)
- iOS (ver.14.4)
- Android (ver.11)
試したブラウザ(各最新版)
- Google Chrome
- Mozilla Firefox
- Safari
- Vivaldi
- Microsoft Edge
- IE11
結果
macOS
ブラウザ | Math.tan(-1e300) | Math.cosh(10) |
---|---|---|
Chrome, Edge, Vivaldi | -1.4214488238747245 | 11013.232920103324 |
safari | -1.4214488238747247 | 11013.232920103323 |
Firefox | -1.4214488238747247 | 11013.232920103324 |
末尾の桁に着目してください。
どうやらブラウザのJSエンジンごとに異なる計算結果が出ているようです。
Windows 10
ブラウザ | Math.tan(-1e300) | Math.cosh(10) |
---|---|---|
Chrome, Firefox, Edge, Vivaldi | -1.4214488238747245 | 11013.232920103324 |
IE11 | -4.987183803371025 | 非対応😌 |
モダンブラウザは全て同一の値で、IE のみハチャメチャな結果となりました。
iOS
ブラウザ | Math.tan(-1e300) | Math.cosh(10) |
---|---|---|
Chrome, Firefox, Edge, Safari | -1.4214488238747243 | 11013.232920103323 |
全ブラウザで同じ値となりました。
iOS は全てのブラウザが Webkit ベースらしいので、これは納得しやすい結果です。
Android
ブラウザ名称 | Math.tan(-1e300) | Math.cosh(10) |
---|---|---|
Chrome, Firefox, Edge, Vivaldi | -1.4214488238747245 | 11013.232920103324 |
こちらも何故か iOS 同様全ブラウザで共通の結果となりました(値自体は iOS と異なっています)。
まとめ
- macの結果を見て「おっ、JSエンジンの判別ができるのかな?」と思ったけどそんなことはなかった
- モバイルに限定すると、ブラウザごとの差異はなかったがOSの判別はできた
完全な判別はできなかったものの、ユーザーエージェント文字列と異なり偽装等の問題も少なそうなので補助的な特徴点としては使えるのかもしれません(ただ情報量としてはそれほど大きくないようです2)。
ちなみにいまお使いのブラウザによる計算結果は以下です。
See the Pen Trigonometry Fingerprint by Masa-Shin (@POPOPON) on CodePen.
今回調査していないOS/ブラウザをお使いの方はぜひ結果をコメント等で教えてください。
補遺A: ブラウザ/OS等により値が異なる背景
EcmaScript の仕様では、例えばMath.tan(x)
は「実装に依存した近似値」を返すと定義されています。
つまり具体的にどういう手法で計算し誤差をどう丸めるかについては何も規定されていません。
また IEEE 754 標準も三角関数の(実装は推奨しているものの)計算手法自体については特に規定していないかと思います[要出典]。
そのためブラウザ/OSが三角関数の計算や端数処理をどう実装しているかにより、返り値が変わる事態になってしまっているようです。
追記:CPUも関係していそう
某サイトで「macOSでもM1チップだとtan
の値が違った」という興味深いコメントを目にしました。
また 32bit CPU の場合も値が異なるらしいので、ブラウザ/OSなどソフトウェア面だけではなくハードウェア面の性質も捉えた特徴点となっているようです。
関連ツイート
ブラウザ(JS)が、もあるけど、CPUが、というのもあるかと思う。
— Justy (@i_justy) February 17, 2021
浮動小数点レジスタのビット数とか、丸めモードのフラグとか。
【閲覧者のユーザーエージェントを三角関数で判定できるか? - Qiita】 https://t.co/ElrdzPlId1
補遺B: 実際の値は?(改稿版)
任意精度計算ができるという Python パッケージ mpmath を利用し計算してみました。
from mpmath import mp
mp.dps = 300 # 有効桁数
print(mp.tan("-1e300"))
print(mp.cosh(10))
(この節のコードはkasai氏の助言を受け改稿しています。ありがとうございました)
-
有効桁数を
300
にしているのは、実際に必要な桁数よりも有効桁数を大きくした方が正確な値が求まりやすいためです(mpmathのドキュメント)。 -
-1e300
は桁数が大き過ぎるため mpmath では文字列として渡す必要があります。
結果
Math.tan(-1e300) | Math.cosh(10) |
---|---|
-5.8600819259448981 | 11013.232920103323 |
-
Math.tan(-1e300)
はブラウザが計算した値と大きく異なるものとなりました。 -
Math.cosh(10)
については「macOS の safari」および「iOS の各ブラウザ」が見事ピタリ賞を獲得しました🎉