navigator.getBattery() 曾無需權限提示即可讀取電量與充電狀態。Firefox 已移除,WebKit 從未實作,這是給新 API 的一課。
曾經有那麼幾年,任何網站都能問你的筆電或手機一個略顯私密的問題——「你還剩多少電,現在在充電嗎?」——而且能立刻得到答案,不需要任何權限彈窗。Battery Status API 原本的設計動機相當正當:讓頁面在使用者裝置快沒電時,調暗動畫或延後一次背景同步。但研究者證明它同樣能被當作一種短時追蹤訊號來讀取,不到幾年,一家主流瀏覽器就將它移除,另一家則從未實作過。這是「立意良善的 Web API 因隱私代價浮現而被回滾」最清楚的真實案例之一。
核心要點
navigator.getBattery()曾向任何腳本暴露level、charging、chargingTime與dischargingTime,全程零權限提示——這種組合在硬體相關 API 中相當罕見。- 2015 年的一項學術研究(「The Leaking Battery」,作者 Olejnik、Acar、Castelluccia 與 Diaz) 證明這些讀數能在短時間窗口內跨來源重新識別同一瀏覽器;而在以完整精度回報讀數的瀏覽器上,腳本還能反推出電池的實際容量——這是壽命長得多的訊號。
- Firefox 在 52 版(2017 年 3 月)移除了網頁內容存取此 API 的權限,理由是隱私風險;Safari/WebKit 從未實作過它,認為這塊指紋暴露面並不值得。
- 以 Chromium 為核心的瀏覽器至今仍提供此 API——Chrome、Edge、Opera 與三星瀏覽器都支援
navigator.getBattery()。 - BrowserInsight 的指紋檢測會顯示你自己的瀏覽器目前還暴露了哪些這類低調訊號。
navigator.getBattery() 曾暴露什麼
這個 API 在 navigator 上掛載了單一個回傳 Promise 的方法。在支援它的瀏覽器上,讀取只需一行程式碼,且不需要任何使用者互動:
navigator.getBattery().then((battery) => {
console.log(battery.level); // 0 到 1,例如 0.73
console.log(battery.charging); // true / false
console.log(battery.chargingTime); // 充滿所需秒數,或 Infinity
console.log(battery.dischargingTime);// 耗盡所需秒數,或 Infinity
});
四個值在起作用:一個小數形式的電量 level、一個布林型的 charging 狀態,以及兩個隨瀏覽器持續觀測電池行為而不斷重新計算的時間估算值。這個 API 還會觸發 levelchange、chargingchange、chargingtimechange 與 dischargingtimechange 事件,所以腳本甚至不需要輪詢——只要掛上監聽,就能即時看著訪客的電量一點一點掉下去。
它的存取門檻很低。這個 API 僅限安全內容環境(也就是需要 HTTPS),也不對 Web Worker 開放;但在一個普通的 HTTPS 頁面裡,沒有權限提示,不要求使用者手勢,介面上也不會有任何跡象顯示頁面剛剛讀取過什麼。更關鍵的是,直接嵌進頁面的第三方分析或廣告腳本——這正是最常見的部署方式——擁有與網站自身程式碼完全相同的存取權限。(現行規範後來補上了名為 battery 的權限政策特性,預設允許清單是 ["self"],至少把跨來源 iframe 擋在門外,除非嵌入方主動放行。)
The Leaking Battery 研究
這裡的隱私問題不在於單次讀數本身有多大識別力——某一刻電量剛好是 73% 的人多得是。問題在於這四個值組合起來、持續刷新,構成了一個短暫卻精確的狀態向量,讓兩個不同的網站(或同一網站上的兩個不同追蹤器)即便在使用者已封鎖 Cookie、開了隱私視窗或在分頁之間來回切換的情況下,也能拿來比對配對。在短至幾秒、長至一兩分鐘的時間窗口內,匹配的 level 加上高度接近的 chargingTime/dischargingTime 估算值,就足以把兩次原本被認為互不相關的造訪關聯回同一台裝置。
最引人關注的發現,出在這些數值有沒有四捨五入上——答案是沒有。在執行於 Linux 上的 Firefox 中,level 是以完整的雙精度浮點數直接交給腳本的,資料幾乎原樣透傳自作業系統的電源管理服務,於是頁面拿到的不是乾淨的 0.73,而是一長串小數。這一點之所以要命,是因為 level 本身就是「目前電量 ÷ 電池滿容量」的比值:小數位夠多,腳本就能從這個比值反推出容量本身——那是實體硬體的屬性,而不是目前電量,從一次瀏覽工作階段到下一次幾乎不會變動。
研究者指出,這一招恰恰對最不容易察覺的那批使用者下手最重。電池容量會隨著使用而衰退,偏離出廠標稱值的方式又因每一顆電池而異,所以一顆用舊了的電池反推出的數字比新電池更「特別」,也就更具識別力。他們給出的建議並不是刪掉這個 API,而是把回報的讀數做粗——這對實際功能毫無損失:一個想在電量 20% 時調暗動畫的頁面,根本不需要精確到小數點後好幾位。
Firefox 為何移除它、WebKit 又為何從未實作
Firefox 是最早的實作者之一:帶前綴的 navigator.mozBattery 從 2012 年的 Firefox 11 起就預設開啟,基於 Promise 的 navigator.getBattery() 則在 Firefox 43 落地——Chrome 早一年就已在 Chrome 38 中提供 getBattery()。指紋研究發表後,Mozilla 的第一個反應正是研究者建議的那個小修正:把讀數的精度砍掉。更大的判斷是後來才做的。2017 年 3 月釋出的 Firefox 52 索性移除了網頁內容呼叫 navigator.getBattery() 的能力,認定其隱私代價已不再值得為多數網站保留這點邊際效用。這個功能仍可由 Firefox 自身的特權程式碼使用,所以這次改動通常被描述為收回網頁內容的存取權,而不是徹底刪掉這個 API。
蘋果的 WebKit 引擎從一開始就走了更保守的路線:它從未向 Safari 提供過 navigator.getBattery(),這與 WebKit 一貫的立場一致——只要判斷某個 API 相對其效益帶來了明顯的指紋風險,就拒絕實作。
以 Chromium 為核心的瀏覽器是例外。caniuse.com 的相容性資料表目前仍顯示這個 API 在 Chrome、Edge、Opera 與三星瀏覽器中受支援——依撰寫時 caniuse 統計的全球瀏覽器使用份額計算,約占 78%——而 Firefox 以及包括 iOS 上 Safari 在內的所有 Safari 變體均回報不支援。這種「Chromium:支援,Firefox:已移除,WebKit:從未實作」的三方分裂,意味著 navigator.getBattery 是否存在這件事本身,就是一種粗略、難以偽造的引擎檢測訊號,與 Network Information API 僅 Chromium 獨有的分布如出一轍。
這給新 Web API 上的一課
Battery Status API 的回滾之所以是個有用的案例研究,是因為它公開發生、時間線相當短,而且原因跟任何實作者的惡意毫無關係——這個 API 完全按規範上線,只是在獨立研究者仔細審視「持續、免權限地讀取一個數值型感測器,跨來源組合起來能做到什麼」之後,風險才浮現。W3C 目前的工作草案現已直接體現這一教訓,明確寫道使用者代理「不應暴露電池狀態資訊的高精度讀數,因為那會引入新的指紋向量」,並且「可以混淆所暴露的值」,讓腳本無法確定自己看到的究竟是不是真實硬體資料。
這正是值得推廣的規律:任何持續回報硬體真實數值讀數的 API——電池、感測器、效能計數器——都不能只問「單次讀數有沒有識別力」,還要問「讓兩個不同腳本觀察這個值隨時間的變化,能不能認出是同一個瀏覽器」。分桶與四捨五入——Network Information API 的設計者對 downlink 與 rtt 採用的正是同一種緩解手段——如今已成為這類 API 的預設期待,而非事後補丁。
這對你今天意味著什麼
如果你用的是 Firefox 或 Safari,這個特定訊號早已關閉——兩款瀏覽器都不暴露它,無需任何設定。如果你用的是以 Chromium 為核心的瀏覽器,navigator.getBattery() 依然能被你造訪的任何頁面呼叫,不過它對綜合瀏覽器指紋的貢獻遠小於 Canvas 或 WebGL,因為電量與充電狀態會持續變動,不像那些訊號在工作階段之間保持穩定。對付這類次要訊號,同樣的常規防禦手段就夠用了:限制頁面可執行內容的腳本/內容封鎖擴充功能,或是一款預設設定就更嚴格的隱私導向瀏覽器。
它還有一層反向用途,這也是 BrowserInsight 的指紋檢測在該 API 存在時會去讀它、而不是直接略過的原因。這四個值彼此之間必須自洽:一台裝置如果同時聲稱自己沒在充電、電量又不滿,卻回報「距離充滿 0 秒」與「距離耗盡無限久」,那它描述的是一顆物理上不可能存在的電池。真實硬體不會給出這種組合,被腳本偽造的瀏覽器環境才會。於是,當年讓這個 API 變成追蹤風險的那幾個讀數,如今又成了一項成本極低的一致性檢查——這也再次說明,一個對外說謊的瀏覽器,往往不是敗在某一個值上,而是敗在指紋自相矛盾上。
常見問題
Battery Status API 現在還能在哪些瀏覽器上用?
還能。Chrome、Edge、Opera 與三星瀏覽器仍然實作了 navigator.getBattery()。Firefox 在 52 版(2017 年)移除了存取權限,Safari/WebKit 則從未實作過它。
網站能精確知道我還剩多少電嗎?
在仍然支援這個 API 的瀏覽器上,可以——level 會以 0 到 1 之間的小數形式回報你的電量,不需要任何權限提示。在 Firefox 與 Safari 上,這個 API 根本不存在,自然無從呼叫。
Battery Status API 在實務上真的被大規模用於追蹤過嗎?
沒有證據顯示它在瀏覽器採取行動之前曾被廣泛濫用為主要追蹤手段——促成這次回應的是學術研究揭露出的風險,而非有據可查的大規模利用案例。瀏覽器是主動回滾,而非事後補救,這也是它成為一個有用案例研究的部分原因。
封鎖 JavaScript 能擋住這個訊號嗎?
能。由於呼叫 navigator.getBattery() 需要執行腳本,停用 JavaScript 或使用封鎖腳本的擴充功能,就能在仍然保留這個 API 的瀏覽器上阻止任何頁面讀取它。
總結
Battery Status API 是 Web 平台歷史上一個不起眼、幾乎被遺忘的片段,但它清楚說明了一個更大的道理:一個免權限的 API 並不需要暴露你的姓名或位置就能成為隱私問題——一個持續更新的數值型感測器,只要能被夠多來源讀取,就足以起到同樣的作用。Firefox 的移除與 WebKit 的拒絕實作,是瀏覽器早期將指紋暴露面當作一等設計限制、而非邊緣情況來看待的具體例證——如今這種立場已經直接體現在 Network Information 這類新 API 從一開始的規範方式中。執行 BrowserInsight 的指紋檢測,看看你自己的瀏覽器還暴露著哪些這類低調訊號。
推薦閱讀:


