Safari 17の高度なオーディオフィンガープリンティング保護機能を回避
(fingerprint.com)- Safari 17はプライベートブラウズモードでAudio APIの各サンプルにランダムノイズを加えてオーディオフィンガープリントを揺らすが、FingerprintJSはこれを低減する新しいフィンガープリントアルゴリズムで対抗した
- 従来方式は500個のオーディオサンプルの合計を識別子として使うため、Safariのノイズ幅がブラウザ間の差より大きくなり、安定性を失う
- 新方式は同じオーディオサンプルのノイズ付きコピーを大量に作り、
(min+max)/2と有効数字での丸めによって値の揺れを抑える - square
OscillatorNode、DynamicsCompressorNode、BiquadFilterNodeを接続してブラウザ間の差を大きくし、選択したブラウザ群の3396番目のサンプルの最小差を0.0014%まで拡大した - 新アルゴリズムはFingerprintJS 4.2.0から従来のオーディオフィンガープリントを置き換えており、計算時間は1.5〜2倍に増えるが、低性能デバイスでも短時間で完了する
Safari 17がオーディオフィンガープリントを揺らす仕組み
- オーディオフィンガープリンティングは、ブラウザのAudio APIとOfflineAudioContextでオーディオ信号をレンダリングした後、サンプルを合算して1つの識別子数値を作る方式
- この識別子はCookieの削除やシークレットモードへの切り替えでも変わらない安定性を持つ一方、多くのユーザーが同じ値を共有しうるため一意性はそれほど高くない
- Safari 17の高度なフィンガープリント保護は、デフォルトでプライベートブラウズモードでは有効、通常モードでは無効で、デスクトップとモバイルの両方に適用される
- この保護機能はScreen APIやCanvas APIにも影響するが、ここではAudio APIのみを扱う
- 保護機能が有効になると、Safariは各オーディオサンプルに個別のランダムノイズを乗算する
- ノイズが適用されたサンプルは
sample*(1-magnitude)とsample*(1+magnitude)の間にある - 分布は一様分布
- Safariの開発は継続中のため、実装の詳細は今後変わる可能性がある
- ノイズが適用されたサンプルは
ノイズが適用されるAudio APIのポイント
- Safariは、オーディオ信号を読み取れる複数のインターフェースでノイズを適用する
- AudioWorkletNode: magnitude
0.001 - AudioBuffer::getChannelData: magnitude
0.001 - AudioBuffer::copyFromChannel: magnitude
0.001 - RealtimeAnalyser::getFloatFrequencyData: magnitude
0.25
- AudioWorkletNode: magnitude
- ノイズは適用されるたびに変わるため、Safari 17のプライベートブラウズモードではオーディオフィンガープリントが計算のたびに変化する
- M1 MacBook AirのSafari 17では、フィンガープリントは
124.03516〜124.04545の範囲で変動し、差は約0.008% - ブラウザごとの従来のオーディオフィンガープリント差のうち最小値は
0.0000023%で、Safariのノイズ幅よりはるかに小さい - ノイズを消すには小数点1桁レベルまで丸める必要があるが、6桁未満しか残さないと一部のブラウザを区別しにくくなり、一意性が低下する
新アルゴリズムの3段階
- FingerprintJSの新しいオーディオフィンガープリントアルゴリズムは、Safariが追加したノイズを減らすために3つの段階を踏む
- ノイズの分散を減らす
- ブラウザ識別子数値どうしの距離を広げる
- 残ったノイズを丸めで取り除く
- 従来のオーディオフィンガープリントは500個のオーディオサンプルの合計なので、各サンプルに一様分布ノイズが加わると、全体のフィンガープリントノイズは正規分布に近づく
- 正規分布の平均は多くのサンプルの平均で推定する必要があるが、一様分布の平均は
minとmaxを使った(min+max)/2で、より少ないサンプルから高精度に推定できる - 実験コードでは、同じ精度条件で正規分布は
524,288個のサンプルが必要だったのに対し、一様分布は4,096個で十分だった - 新方式は合算フィンガープリントの代わりに単一のオーディオサンプルだけを収集し、一様分布ノイズを扱うように変更した
- この変更により新しいフィンガープリントは従来のフィンガープリントと互換性がなく、訪問者識別子を失わずに移行するには別のアプローチが必要になる
同じオーディオサンプルのノイズ付きコピーを作る
AudioBufferインスタンスでgetChannelDataを複数回呼び出す方法は機能しない- ノイズは各
AudioBufferインスタンスごとに1回だけ適用される - 同じインスタンスの
getChannelDataは同じ信号を返す
- ノイズは各
- オーディオ信号生成プロセス全体を何度も実行すれば多くの
AudioBufferインスタンスを作れるが、フィンガープリント計算用としては遅すぎる- 6,000個のノイズサンプルではM1 MacBookで最速でも7秒だった
- 60,000個ではSafariが処理を終えられなかった
- より良い方法は、同じオーディオ信号を繰り返す
AudioBufferインスタンスを作ること- 1つ目のオーディオ信号をレンダリングするが、
getChannelDataは呼び出さない - より長い2つ目のOfflineAudioContextを作り、元の信号をAudioBufferSourceNodeとして使う
loop、loopStart、loopEndで元の信号の一部を繰り返す- 繰り返しの後でSafariがノイズを加えるため、同じオーディオサンプルに異なるノイズが適用されたコピーを得られる
- 1つ目のオーディオ信号をレンダリングするが、
- この方法なら、オーディオレンダリング2回だけで必要な数のノイズ付きコピーを作れる
- ノイズが完全に消えるわけではないが、元のオーディオサンプルより分散は小さくなる
8,192個のコピー: 100回実行結果の範囲0.000387%、M1 MacBook基準2.6ms65,536個のコピー:0.0000123%、4.1ms262,144個のコピー:0%、7.5ms
ブラウザ間のオーディオサンプル差を大きくする
- コピー数を減らすと性能は良くなるが結果の分散が大きくなるため、ブラウザ間のオーディオサンプル差を拡大する目的でベース信号を変更する
- 複数の組み込みオーディオノードを試した結果、ブラウザ間のサンプル差が大きくなる信号生成チェーンは次の通り
- square OscillatorNode
- DynamicsCompressorNode
allpass型 BiquadFilterNode
- オーディオ信号の3396番目のサンプルがブラウザ間の差が最も大きく、複数ブラウザの全サンプルを比較して見つけた値だった
- 選択したブラウザ標本では、この新しいサンプルの最小差は
0.0014%だった- 従来フィンガープリントの最小差
0.0000023%より大きい - そのため、より粗いノイズ除去と丸めを適用できる
- 従来フィンガープリントの最小差
丸めによるフィンガープリントの安定化
- 単一サンプルのノイズ幅が小さくなっても値は依然として揺れるため、最終的なフィンガープリントとして使うには丸めが必要
- ノイズは絶対値ではなくオーディオサンプル値に対して相対的に適用されるため、小数点以下の桁数ではなく有効数字を基準に丸める
- 選択したブラウザ群を区別するには有効数字5桁で十分だったが、全ブラウザや将来の変化までは確認できないため、より多くの桁を使う
- Safari 17のプライベートブラウズモードで、丸め精度ごとに安定化に必要なコピー数は次の通り
- 有効数字6桁:
15,000個、Safari 17 on M1 MacBook warm基準3ms - 有効数字7桁で最後の桁を5の倍数に丸める:
30,000個、4ms - 有効数字7桁で最後の桁を最も近い偶数に丸める:
70,000個、6ms - 有効数字7桁以上:
400,000個、12ms
- 有効数字6桁:
- 最終的な選択は有効数字7桁で最後の桁を
0または5にする方式で、安定性を高めるためコピー数を40,000個に増やす - このように丸めた数値は、Safari 17の高度なフィンガープリント保護が有効でも変化しない新しいオーディオフィンガープリントになる
- 新しいフィンガープリントは、従来のオーディオフィンガープリントと同等の一意性を持つと評価されている
性能と実行上の制約
- 空のページでブラウザがwarmな状態を基準にすると、新アルゴリズムは従来より概ね遅い
- MacBook Air 2020 Safari 17.3: 従来
2ms、新方式4ms - MacBook Air 2020 Chrome 120: 従来
5ms、新方式8ms - iPhone 13 mini Safari 17.3: 従来
8ms、新方式12ms - Galaxy J7 Prime Chrome 120: 従来
33ms、新方式45ms - BrowserStack Windows 11 Firefox 121: 従来
10ms、新方式18ms
- MacBook Air 2020 Safari 17.3: 従来
- 新アルゴリズムの性能は従来比で1.5〜2倍悪化するが、低性能デバイスでも計算時間は短い
- ブラウザが一部の処理を
OfflineAudioRenderスレッドに委譲するため、オーディオフィンガープリント計算の大半の間もページの応答性は保たれる - Web Audio APIはweb workersで使えないため、オーディオフィンガープリントをワーカー内で計算することはできない
- 性能改善のためには、ユーザーエージェント文字列でSafari 17以上かどうかを判定し、Safari 17以上でのみ新アルゴリズムを使い、それ以外のブラウザでは従来アルゴリズムを維持できる
TorとBraveの違い
- TorはWeb Audio APIを完全に無効化するため、オーディオフィンガープリンティングは不可能
- BraveはSafari 17のようにオーディオ信号へノイズを追加するが、方式は異なる
- Safariは各オーディオサンプルごとに別々のランダム値を乗算する
- Braveは
fudge factorというランダムな乗数を1回生成し、すべてのオーディオサンプルに同じ値を乗算する- この値はページ内で維持される
- 新しい通常セッションまたはシークレットセッションでのみ変わる
- Braveでは、どれだけ多くのオーディオサンプルのコピーを作っても全コピーに同じノイズが適用されるため、Safari向けの数学的ノイズ除去方式は機能しない
- ただし以前のBrave向けノイズ除去方式は引き続き機能し、ブラウザ間のフィンガープリント差を大きくする新しい信号生成方法は許容誤差を広げられる
FingerprintJSへの適用
- 新しいオーディオフィンガープリントアルゴリズムはFingerprintJSで従来方式を置き換えており、4.2.0で初めて公開された
- 実装全体のコードはFingerprintJSのGitHubリポジトリにある
- オーディオフィンガープリントは、オープンソースライブラリがブラウザフィンガープリントを作る際に使う複数のシグナルの1つ
- FingerprintJSは、ブラウザから取得できるあらゆるシグナルを無条件に含めるのではなく、各シグナルの安定性と一意性を個別に分析し、フィンガープリント精度への影響を判断している
- オーディオフィンガープリントは一意性への寄与は小さいが、安定性が高く、全体のフィンガープリント精度をわずかに高めるシグナルと評価されている
1件のコメント
Hacker News のコメント
オンラインでユーザーを識別する別の興味深い手法として GPU フィンガープリンティングがあり、2022年に「DrawnApart」というコードネームで紹介された
WebGL で GPU の実行ユニット数と速度を数え、頂点レンダリングの完了時間や stall 関数の処理などを測定する方式
最近は特に サイドチャネル攻撃への関心を見ると、データが漏れる値に一様ノイズを加える方式は、ほぼ当然ながら効かないと思う
サンプルを多く集めればノイズを除去できるから。Safari がなぜこれを入れたのか分からない。フィンガープリンティングを面倒にはできるかもしれないが、この記事のように結局は何らかの形でおおむね突破可能に見える
技術的に効果があるかよりも、大衆にそれらしい話をできるかが重要になった プライバシー劇場のように感じる
そもそも、なぜ結果が違ってくるのか説明してもらえる? たとえば オーディオフィンガープリンティングがなぜ可能なのか気になる
Web Audio API で小さな信号を作ると、どのブラウザもほぼ同じ結果を出すが、ごく小さな差を使って相互に識別できる
ブラウザ開発者がこれを防ぐために、オーディオバッファ処理にノイズを加えなければならないのは残念
要約すると、同じコードベース内でも異なるコードパス、たとえば SIMD 版が微妙に異なる 浮動小数点結果を生み得る。浮動小数点演算は演算順序などに予想以上に敏感だという点に関係していそう
同じアルゴリズムと同じ公式を正しく実装しても、結果が少しずつ異なることがある
間違っていたら直してほしい。ここでフィンガープリンティング回避が成功する理由は、Web Audio API の仕様で OscillatorNode のアンチエイリアシング処理方法をこのように開いた選択にしたことに帰着するように思う
「このエイリアシングを避けるために実装が取り得る実用的なアプローチはいくつもある。アプローチにかかわらず、理想的な離散時間デジタルオーディオ信号は数学的によく定義されている。実装上のトレードオフは、CPU 使用量という面での実装コストと、この理想にどれだけ忠実に到達するかにある。実装がこの理想を達成するためにある程度注意を払うことは期待されるが、低性能ハードウェアでは、より低品質で低コストなアプローチを検討するのが妥当である」
私には、これはここで悪用している OscillatorNode の出力が、ブラウザ間で、さらには同じブラウザでも異なるハードウェア上ではほぼ確実に決定的ではないという意味に見える。非決定性は、ブラウザが選んだアンチエイリアシング方式、またはハードウェアに応じて同じブラウザ内で選択される複数の経路に基づく。同じアンチエイリアシングアルゴリズムの変更や修正も含まれる
なぜアンチエイリアシングをブラウザに任せたのかはよく分からない。高品質なオーディオアプリやライブラリは、自分が生成する信号のエイリアシング回避方法を完全に制御したがるだろうし、標準のオシレーターは使わないはず。逆に、任意のアンチエイリアシングアルゴリズムとそれに伴うブラウザごとの差を受け入れる Web アプリなら、そのアルゴリズムがハードコードされた SIMD 命令であれ、20MB の JavaScript Web Audio ヘルパーフレームワークであれ、あまり気にしない可能性が高い
1: https://webaudio.github.io/web-audio-api/#OscillatorNode
HTML5 パーサーを標準化するときに Hixie が使ったのと同じ解法を、ここに適用できるのかも気になる。特定分野の専門家が十分良好に動作する正確で決定的なアンチエイリアシングアルゴリズムを指定し、その後すべてのブラウザがそれを使う、という形だ。測定可能な性能低下は、標準のアンチエイリアシング付きオシレーターで信号を生成する Web Audio API チュートリアルくらいでしか見えなさそう
だから、利用可能な演算リソースやバッテリーなどに応じて、実装がどれだけ使うかを決められるようにしたいのだと思う
ブラウザに ノードグラフ型のオーディオ APIを入れたのは愚かだった。AudioWorklet だけがあるべきだった
https://web.archive.org/web/20120505042746/https://developer...
気持ち悪い
そもそも、なぜ音声 API がウェブサイトの権限なしに使えるのか分からない。「このサイトがサウンドデバイスを使用しようとしています」のような簡単なダイアログで簡単に直せそうに見える
現在の形のインターネットは、パーソナルコンピューティングの夢をかなり台無しにした。企業や国家が個人に比べてあまりにも非対称に強いからだ。自分の技術が明示的な承認なしにサーバーへデータを送れるべきなのだろうか?
一方で、ブラウザキャッシュを消して VPN を有効にしたら、私を新規訪問者だと誤認してはいた。それでもビジネスモデルは卑劣だ
たとえ希望的観測だとしても、こうした研究を公開して表に出すことには大きな価値がある。特定ブランドの緑色のバックパックが窃盗に役立つという記事が出たからといって、みんながもっと盗むようになるのではと心配するより、店側がその手口により気づきやすくなる可能性に賭けたい
各サンプルにランダムな値を加える代わりに、Safari は毎時変わるキーに基づく決定論的ノイズを加えることもできそうだ
音声サンプルとキーの関数にすれば、同じセッションではノイズが同じだが、1時間後の追跡には役に立たなくなる
これを直すには情報漏えいそのものをなくす必要があり、ランダムな偏差の層で覆い隠すだけでは不十分だ
例えば
RNG_SEED = HMAC_SHA256(PERSISTENT_SECRET,Location.origin)のような方式だいよいよ本当に JavaScript をオフにしてウェブを見る「そういう人」になる準備ができた
ほかの場所から数ビットかき集めるだけで、一意に識別され得る。それでも私の基準では、この人たちは広告技術業界の残りと一緒に Golgafrinchan Ark B に乗せて送り出していい
少し前に訪れたウェブサイトはマークアップを使ってはいたが、それを HTML にコンパイルして静的に配信するのではなく、クライアント側 JavaScript でレンダリングしていた。WTF
Cloudflare のような DDoS チェックだけでなく、今ではページ HTML の中にあるべきものまで JavaScript で読み込まれる
インターネットがますます敵対的になるほど、この選択はますます正しい方向になる
この方法で数千以上の固有の組み合わせを作れるというのが、よく理解できない
ブラウザ種別 × ブラウザバージョン × OS バージョン × アクセラレータのバージョン × …あと何がある? リモートで一意だと言えるほどの変動が十分にあるようには見えない
この手法は、音声処理におけるハードウェア、ドライバ、OS の違いを基準にフィンガープリントを取るものなのか、それとも単にブラウザソフトウェアだけを見ているのか?
下位のグラフィックデバイスの違いを露出する似たような手法があった、あるいは今もあると思う
記事で挙げられている例の1つが高速フーリエ変換(FFT)だ。すべての OS にはこの関数のバージョンがあるが、時間とともに最適化される傾向があり、利用可能な SIMD 命令によって CPU ごとに異なる動作をすることが多い