navigator.audioSession 讓網頁宣告音訊類型並觀察 interrupted 狀態。它會洩露哪些裝置資訊,界線又在哪裡。
正在播放音訊的網頁,可以得知瀏覽器之外的某件事奪走了音訊通道——例如來電,或另一個應用程式獨占了音訊。整個過程不需要權限彈窗,也不需要麥克風權限。這是一個出於合理目的設計的 API 所帶來的副作用:它原本是為了告訴作業系統「這個頁面在發出哪一類聲音」。本文介紹 Audio Session API 暴露了什麼、不能告訴頁面什麼,以及它為什麼和其他「本為適配裝置、卻兼作被動觀察」的 API 屬於同一類。
核心重點
navigator.audioSession讓頁面透過type屬性宣告音訊類別:auto、playback、transient、transient-solo、ambient或play-and-record。作業系統據此決定要壓低、暫停還是混合其他音訊。- 工作階段還有
state——active、inactive或interrupted——狀態變化時觸發statechange事件。 interrupted由瀏覽器之外的事件觸發,例如來電或其他應用程式搶佔音訊焦點。頁面只知道「有東西優先了」以及時間點,不知道是誰來電、被什麼打斷。- 各引擎支援不同。 WebKit 在 Safari 16.4 中發布了該 API 的子集,MDN 至今仍將其標為實驗性、可用性有限,而什麼算作「中斷」由平台決定,因此該 API 是否存在及其行為本身就是一種粗粒度的引擎訊號。
- 沒有關閉它的開關。 只要頁面上有音訊,這種行為就是固有的,現實的防禦只有了解它的存在,再加上通用的反指紋措施。
Audio Session API 的本意
在這個 API 出現之前,瀏覽器只能猜測頁面的音訊該如何與裝置上的其他聲音相處。視訊通話頁面、Podcast 播放器和只播放一聲提示音的頁面,從外部看起來很相似,但手機應當區別對待:通話要優先並讓其他應用程式靜音,Podcast 在來電時應暫停,提示音則只需讓其他音訊短暫壓低。
AudioSession 讓頁面自己說明在做什麼。type 屬性可取以下值:
| 類型 | 預期用途 |
|---|---|
auto | 由瀏覽器根據播放內容自行決定 |
playback | 音樂、影片、Podcast——使用者主動想聽的音訊 |
transient | 通知等短促聲音,其他音訊會被壓低 |
transient-solo | 需要短暫讓其他一切靜音的短促聲音 |
ambient | 可與其他應用程式混合的背景聲音 |
play-and-record | 雙向音訊,例如通話 |
W3C 規格名為「Audio Session」,WebKit 在 Safari 16.4 發布說明中宣布支援其子集。宣告類型是這個故事合法的一半,設計也很好:它把資訊交給平台,而不是讓平台去推測意圖。
另一半:工作階段狀態
同一個物件還會回報工作階段目前所處的狀態:
if ("audioSession" in navigator) {
console.log(navigator.audioSession.type); // e.g. "playback"
console.log(navigator.audioSession.state); // "active", "inactive" or "interrupted"
}
共定義了三種狀態:active 表示頁面正在播放或錄製音訊,inactive(預設值)表示兩者皆無,interrupted 表示平台暫時擱置了一個原本作用中的工作階段。狀態轉換時會觸發 statechange 事件,頁面無須輪詢。工作階段被中斷期間,瀏覽器會自動暫停頁面的媒體元素、暫停其 AudioContext 並將麥克風音軌靜音,待狀態恢復為 active 後再自動恢復。
真正有隱私分量的是 interrupted。MDN 舉出的觸發範例是來電,以及另一個應用程式獨占音訊。相關的 Web Audio 屬性 AudioContext.state 也用了同一個詞,定義為「網頁應用程式無法控制的外部事件」造成的中斷,MDN 還在該頁註明:各瀏覽器觸發 interrupted 狀態的方式可能不同。具體觸發條件由平台和瀏覽器決定,所以各處並不一致。
為什麼這是隱私問題
關於使用者在裝置上做什麼,大多數訊號要嘛需要權限,要嘛網頁根本拿不到。你是否正在通話,本不該是頁面知道的事。而 Audio Session API 作為播放聲音的副作用,卻洩露了這項資訊的模糊版本。
需要精確劃定界線,因為這件事很容易被誇大:
- 頁面無法得知是誰來電,也不知道中斷的原因。它看到的是狀態變化,而不是起因。
- 只有頁面持有音訊工作階段時才有效。 沒有音訊的頁面沒有什麼可被打斷。
- 單次事件是很弱的證據。 來電和其他應用程式搶佔音訊焦點,看起來可能一模一樣。
剩下的是一條行為時間線。由於頁面也能看到狀態恢復為 active,它可以替每次中斷計時。反覆出現的中斷、它們的時間點,以及工作階段保持中斷的時間長度,合起來就是裝置何時在忙別的事的紀錄。它本身不是識別碼,但正是分析與反詐欺系統樂於蒐集的那類低強度、無須提示的訊號,也是使用者不會想到頁面能看到的訊號。
更隱蔽的訊號:特性偵測
還有第二條更常規的洩露途徑。navigator.audioSession 是否存在,以及 interrupted 狀態如何觸發,取決於引擎。WebKit 在 Safari 16.4 中發布了子集,MDN 將該 API 標為實驗性、尚非 Baseline,並在與之密切相關的 AudioContext.state 頁面上明確指出各瀏覽器觸發中斷的方式可能不同。這種差異正是引擎版本與特性偵測一文所講的模式:某項能力在一個引擎中有、在另一個中沒有,就能縮小你所用瀏覽器和作業系統的範圍,而無須讀取任何 user-agent 字串。
BrowserInsight 的核心檢測對 Chromium 就是這樣做的。它的特性矩陣會偵測 AudioContext.setSinkId() 等音訊 API,把瀏覽器定位到版本時間線上。需要說明範圍:本站不會探測 navigator.audioSession、AudioSession.state 或 AudioContext.state,這裡也沒有任何工具會回報來電或中斷事件。setSinkId() 只是「音訊 API 的存在可作為引擎訊號」的一個具體例子。
與其他裝置狀態 API 同一模式
這並非音訊獨有。反覆出現的教訓是:為了讓頁面適配裝置而設計的 API,同時也成了對裝置的被動觀察:
- Battery Status API 無須提示就把電量和充電狀態交給頁面,後來 Firefox 將其移除,WebKit 從未發布。
- Network Information API 暴露連線類型與估算值,足跡偏向 Chromium。
- 媒體裝置列舉會揭示連接了哪些攝影機和麥克風。
- 裝置感測器會暴露動作和方向讀數。
每個案例的初衷都是真實的。隱私代價來自三者的疊加:無權限提示、持續更新,以及各瀏覽器實作不一。
為什麼行動裝置更嚴重
中斷在手機上頻繁且有意義,在桌機上則少見。來電、其他應用程式爭奪音訊焦點,在行動裝置上時時刻刻都在發生,所以這裡的訊號既更豐富也更有區分度。這也是本文歸入行動裝置專題的原因:同一個 API 在桌面瀏覽器上大多保持安靜。關於手機差異的整體情況,請參閱行動瀏覽器指紋。
你實際能做什麼
老實說,很少。Audio Session API 沒有開關,這種行為源自頁面上存在音訊本身。沒有播放音訊的頁面,也就沒有什麼可觀察的。除此之外,現實的答案就是適用於所有小訊號的通用做法:
不一致同樣重要:宣稱是某個平台、卻暴露另一個平台 API 表面的設定,正是指紋一致性一文所講的那類不匹配。
常見問題
網站能知道我正在講電話嗎?
不能直接知道。正在播放音訊的頁面可以看到自己的音訊工作階段變為 interrupted,來電可能導致這一點。它能替這次中斷計時,但無法得知是誰來電,也無法判斷起因是來電還是其他事件,例如另一個應用程式接管了音訊。
Audio Session API 需要權限嗎?
不需要。它是 navigator 的屬性,讀取其類型和狀態不會觸發提示。這在很大程度上就是它對隱私重要的原因。
哪些瀏覽器支援它?
WebKit 在 Safari 16.4 中發布了子集。MDN 將該 API 標為實驗性、可用性有限,所以請查看 MDN 頁面上的最新相容性表,而不要想當然。
我能關掉它嗎?
沒有設定項。頁面不播放音訊就沒有可被打斷的內容,封鎖不需要的腳本則能限制誰在觀察。
結論
Audio Session API 是對一個真實問題的合理回答:作業系統應當知道頁面在發出哪類音訊。它的 interrupted 狀態則是意外的附贈品:讓頁面在沒有提示、也不知道起因的情況下,察覺到瀏覽器之外有事物奪取了優先權。這個訊號單獨來看很弱,在手機上比桌機更豐富,並與電量、網路和感測器 API 一樣提醒我們:適配功能和觀察功能往往是同一個功能。
推薦閱讀:


