搜「線上加密」或「瀏覽器加密檔案」,結果頁幾乎都會寫:計算在本地、檔案不上傳、金鑰不離開裝置。這些句子讀起來安心,卻沒法當作證據。頁面可以這麼寫,腳本仍然可以把輸入框裡的密碼、檔案切片或網址列裡的金鑰用 fetch 送出去。你要核對的不是文案,而是這一次操作有沒有把明文送出瀏覽器。

本文不講「怎麼選一個加密產品」,也不複述某個工具頁的功能清單。問題只有一個:宣稱在瀏覽器裡加密時,你能用開發者工具和網址列當場看見什麼。MyPassGen 的檔案加密與閱後即焚都走 Web Crypto API 的 AES-256-GCM,開啟即可使用;下面用同一套核對方法說明界線,而不是讓你相信一句「我們不保存」。

口號沒法核對,流量可以

「用戶端加密」這個詞被用濫了。有的網站把檔案 POST 到自己的伺服器,再在伺服器用 AES 包一層,回一個下載連結——對使用者來說也叫「線上加密」。有的網站在分頁裡呼叫 crypto.subtle.encrypt,下載 .lock 或 .enc,全程沒有業務上傳。兩種流程在行銷頁上可以寫成同一句話,在 Network 裡完全不像。

瀏覽器不會替你審查文案。它只會把這次點選觸發的文件請求、腳本、圖片、XHR / Fetch 列出來。請求列裡有沒有明文,請求主體裡有沒有密碼,分析回傳的 url 裡有沒有 # 後面的金鑰——這些都能看見。看不見的才是「預設不離開本機」;看見了,口號就可以作廢。

核對不需要讀完整支原始碼。你需要的是一次可重複的操作:打開面板、做一件具體的事(產生密碼、檢測一段密碼、加密一個小檔案、建立一條焚鏈),然後逐條點開請求。下面先分清「本地」到底指哪一層,再對照井字號與問號,最後落到步驟。

「本地」指哪一層

Web Crypto API 是瀏覽器提供的密碼學原語介面,透過 crypto.subtle 做雜湊、派生、加密和解密。W3C 在使用案例裡寫過一種合法架構:使用者在瀏覽器裡選金鑰、加密文件,再把加密後的資料上傳到服務商。也就是說,API 保證的是「加密運算可以在本機發生」,並不禁止之後把密文送出去,更不保證頁面作者不會先上傳再加密。

Web Crypto 還要求安全情境:正式環境裡通常是 HTTPS(本機 localhost 例外)。這不是行銷點,而是介面能不能用的前提。頁面若在普通 HTTP 上聲稱「使用了 Web Crypto」,先看網址列是不是鎖,再談演算法。

AES-256-GCM 在本機意味著什麼

MyPassGen 初版只使用 AES-256-GCM。依 MDN AesGcmParams 與其所引的 NIST SP 800-38D,GCM 是認證加密:密文被改過,解密會失敗,而不是解出一段亂碼還讓你當正文用。初始向量(IV)建議 96 位元,也就是 12 位元組,且同一金鑰下每次加密必須換新 IV;IV 本身不必保密,可以和密文放在一起。認證標籤預設 128 位元。這些數字可以在實作裡核對,不需要當成口號背誦。

檔案加密還會先用密碼派生金鑰。本站檔案盒的派生是 PBKDF2,100,000 次迭代,雜湊為 SHA-256,鹽值 16 位元組,再得到 256 位元 AES-GCM 金鑰;單檔上限 5 GB,輸出 .lock 或 .enc。密碼若被一起 POST 出去,派生次數寫得再高也救不了——所以核對的第一對象仍是流量,不是迭代次數。

三種完全不同的「線上加密」

把流程攤開,至少能分成三類。第一類:瀏覽器只負責選檔,位元組送進伺服器,加密在對面完成。第二類:瀏覽器先加密,再只上傳密文,金鑰走另一條通道(例如網址的 #)。第三類:加密結果直接下載到本機,業務請求裡根本沒有檔案本體。三類都可以叫「網頁上的加密」,只有後兩類有資格談「明文預設不上傳」。你要做的,是用 Network 判斷眼前這個頁屬於哪一類。

先定核對對象,再點開始

產生密碼、檢測密碼、清洗連結、加密檔案,期望是「沒有把明文當業務資料送出」。閱後即焚期望不同:允許看到密文字段,但不該看到原文,也不該在請求列裡看到 # 後面的金鑰。把兩類期望混在一起,會把正常的密文 POST 誤判成「外洩了」。

井字號與問號不是同一類

網址可以拆成協定、主機、路徑、查詢(? 後面)和片段(# 後面)。RFC 3986 §3.5 把 # 後面稱為 fragment:它標識的是取回主資源之後,在用戶端解釋的次級位置。MDN 對 URI fragment 的說明更直白:請求該 URI 時,fragment 不會發給伺服器,由用戶端在資源到達後再處理。

查詢字串正好相反。開啟 https 連結時,? 後面的 名字=值 會進入請求列,也會進對端存取紀錄。上一篇外發連結時哪些參數能刪已經寫過:utm_source 和 id= 都活在 query 裡。金鑰如果寫成 ?key=,伺服器、反向代理和 CDN 日誌都能看見;寫成 # 後面,預設請求列裡沒有這一段。

Referer 也會剝掉 fragment。MDN Referer 寫明:該標頭可以帶來源、路徑和查詢,不可以帶 URL fragment 和使用者名稱密碼。W3C Referrer Policy 在「送出前剝離」步驟裡同樣把 fragment 置空。因此「金鑰放在 # 後面,就不會隨 HTTP 請求發給託管密文的那台機器」是協定行為,不是某家網站的口頭保證。

閱後即焚的連結形態是 s.html?id={id}#{key}:query 裡只有密文編號,金鑰只在井字號後。建立時,瀏覽器先用 32 位元組隨機金鑰做 AES-256-GCM(IV 12 位元組),再把密文 POST 出去;閱讀時,腳本從 location.hash 取金鑰,在本機解密。明文上限 32 KB。你要核對的是:建立請求的 JSON 裡應有密文字段,不應有原文;閱讀頁文件請求的 URL 裡應有 id=,不應有 # 後面那一截。

用 Network 當場核對

Chrome、Edge、Firefox、Safari 都提供網路面板。名稱略有差別,步驟一樣。按 F12 或右鍵「檢查」即可,不必安裝外掛,也不必把檔案交給第三方「檢測站」——把完整網址或檔案再上傳一次,反而擴大了暴露面。

  1. 打開開發者工具,切到 Network(網路)。勾選「保留紀錄 / Preserve log」,避免跳轉後清單被清空。
  2. 過濾選 Fetch / XHR(或「XHR」)。靜態資源可以後看;業務上傳幾乎總走這類請求。
  3. 做一次你要核對的操作:產生密碼、輸入一段可丟棄的測試密碼、貼上一條帶 UTM 的連結、選一個小檔案加密,或寫入一句測試機密再建立焚鏈。
  4. 逐條點開新出現的請求。看 Request URL:文件和 API 位址裡不應出現你剛輸入的明文,也不應出現 # 及後面的金鑰。
  5. 打開 Payload / Request 面板。JSON 或表單裡不應出現原文、密碼、待測密碼或完整檔案位元組。閱後即焚允許出現密文字段,它應像隨機二進位的 Base64,而不是你剛打進去的那句話。
  6. 再掃一遍分析或統計請求(常見名字含 matomo、collect、g/collect)。看回傳的頁面位址或自訂參數裡,有沒有把 location.href 連同井字號一起送出。

檔案加密還有一個更硬的對照:中斷網路或打開飛航模式,再加密一個小檔案。若計算聲稱完全在本機,下載 .lock / .enc 仍應成功。閱後即焚做不到這一步——它必須把密文交給伺服器暫存,斷線時建立失敗是預期,不是打臉。把「完全無請求」當成唯一及格線,會誤傷零知識焚鏈。

密碼檢測頁可能去拉一份靜態弱密碼名單(例如本站的 leaked-top10k.txt)。那是詞表,不是你的輸入。核對時看請求:允許看到詞表檔,不允許在 query 或 body 裡看到輸入框裡的待測密碼。這和全網 HIBP 撞庫不是一回事;後者會把雜湊或密碼片段發到外部介面。

你在做什麼 Network 裡可以出現 不該出現
產生隨機密碼 頁面自身的腳本與樣式 產生結果、勾選的字元集被 POST
本機檢測密碼 靜態弱密碼名單 輸入框裡的待測密碼
清洗連結或遮罩個資 無原文業務請求 完整 URL、證件字號、電子郵件原文
閱後即焚 密文、id、過期與閱讀次數 明文;請求列裡的 # 金鑰
加密檔案 無檔案本體業務上傳 檔案位元組、密碼

MyPassGen 依這張表工作:密碼長度隨機模式 6–128 位(預設 16,低於 8 位會提示較弱);檢測對照本地名單,不是全網撞庫;清洗與檔案加解密預設不把原文當業務資料上傳;焚鏈只暫存密文。全部開啟即用,沒有註冊門檻。表裡的「不該出現」你自己就能在面板裡打勾,不必先接受品牌承諾。

密文出站不等於明文出站

零知識分享和「完全單機」容易被揉成一句。檔案盒屬於後者:結果落到下載目錄,伺服器沒有這份檔案的業務副本。焚鏈屬於前者:對面必須能取出一段密文,接收方才能在自己的瀏覽器裡解密。伺服器看見密文、看不見金鑰,這是設計,不是意外。

判斷外洩的標準因此要拆開。建立焚鏈時,POST 主體裡出現很長的 Base64 是正常的;若同一欄位裡能讀出你剛輸入的「測試用 API Key」,才是明文出站。閱讀頁的文件請求應是 s.html?id=…,Network 列出的 Request URL 在規範下不會帶 #。點開該請求的 Headers,確認請求列和 Referer 都沒有金鑰。

GCM 的認證標籤讓「改幾個位元組再給你解」變得困難:標籤對不上,decrypt 直接失敗。這保護的是完整性和真實性,不是「連結絕對不會被轉寄」。誰拿到完整網址(含井字號後的金鑰),誰就能在閱讀次數用盡之前解密。Network 核的是伺服器有沒有金鑰;聊天紀錄、郵件和截圖是另一條風險,下一節單獨說。

不要把完整焚鏈貼進會上傳原文的「檢測站」

井字號後面的金鑰對 HTTP 伺服器不可見,對你貼上的下一個網站完全可見。要核對應在自己的開發者工具裡看請求,而不是把 s.html?id=…#… 整段交給來路不明的掃描頁。

規範管不到的外洩面

HTTP 不傳送 fragment,並不等於金鑰永遠不離開這台電腦。頁面腳本可以讀 window.location.hash,再自己 fetch 出去——這是實作錯誤,不是協定漏洞。所以第六步要看分析請求:若統計把 location.href 原樣當頁面位址回傳,井字號後面會進統計日誌。正確做法是回傳路徑或丟棄 hash,而不是依賴「反正 HTTP 不會帶」。

瀏覽器歷史、同步的分頁和部分當機報告會保存完整網址。把焚鏈留在網址列,等於把金鑰留在本機歷史裡。分享時用一次性通道,讀完不要把帶 # 的位址寫進工單範本。Referer 雖然剝 fragment,查詢參數仍可能被帶去第三方;金鑰絕不能改放進 ?key=。

螢幕、剪貼簿和 LINE 群紀錄在協定之外。同事把完整連結截進聊天,伺服器依然沒有金鑰,接收方之外的人卻有了。本地加密回答的是「計算發生在哪、預設誰看不見明文」,不回答「拿到完整連結的人會不會轉寄」。檔案密碼也一樣:.lock 可以進雲端硬碟,密碼必須走另一條管道;短機密更適合焚鏈,而不是把密碼寫進同一封郵件本文。

常見誤區

「斷線就不能用,所以一定在上傳明文。」焚鏈必須暫存密文,斷線失敗只說明它依賴一次密文傳輸。要看的是那一次傳輸的內容,不是有沒有請求。

「HTTPS 已經加密,再談本地加密是重複。」HTTPS 保護的是傳輸路徑上的竊聽者。伺服器作為 TLS 終點,仍能看到請求主體和 query。本地加密要擋住的是「伺服器或中間分析預設讀到明文」這一層,和 TLS 疊在一起,職責不同。

「頁面寫了 Web Crypto,就等於明文不上傳。」Web Crypto 只提供原語。先 encrypt 再上傳密文,和先 FormData 再在伺服器加密,都可以呼叫同一個 API 名字做點綴。面板裡的 payload 比頁尾標語優先。

「看不到業務網域的請求,就絕對安全。」擴充功能、系統代理和部分企業閘道不會全部畫在網頁 Network 裡。面板能推翻「完全無上傳」的廣告,不能證明世界上沒有第二條通道。對日常選用線上工具,推翻宣稱已經夠用:看見明文,立刻停手。

從哪一步開始

先選一件今天就要做的、可丟棄的操作。加密檔案最容易對照:選一張無敏感資訊的截圖,輸入臨時密碼,打開 Network,點加密,確認沒有檔案本體 POST,再下載 .lock 或 .enc。需要把同一張圖發給別人時,密文走雲端硬碟,密碼走另一則訊息。單檔不要超過 5 GB。

若你要傳的是一句密碼或復原碼,改用閱後即焚:寫入測試句,產生連結,檢查建立請求只有密文;用無痕視窗打開閱讀頁,看文件請求列是否只有 id=。不要把閱讀頁當成可收錄的內容頁去傳播——它是一次性密文場景。連結清洗則繼續依上一篇的對照表刪 UTM 和點選 ID,同樣在本機完成。

做完一次,你就已經能回答本文的問題:本地加密是不是真的,不看口號,看這一次流量裡有沒有明文、密碼和井字號後的金鑰。演算法用 AES-256-GCM、計算走 Web Crypto、工具開啟即用,都是可以並列核對的約束,而不是需要先註冊才能聽見的承諾。

常見問題

為什麼檔案加密可以斷線,閱後即焚不行?

檔案加密的結果是本機下載;閱後即焚必須讓接收方稍後取到密文,所以建立時會 POST 密文。兩者都可以在瀏覽器裡完成 AES-256-GCM,出站內容的資格不同:前者不應有檔案本體,後者允許密文、不允許明文和金鑰。

井字號後面的金鑰,伺服器真的收不到嗎?

依 URI 與 HTTP 的常規實作,文件請求和 Referer 都不帶 fragment。這可以用 Network 裡那一條文件請求當場看。腳本若主動讀取 location.hash 並回傳,那是頁面自己的行為,也要在 Fetch 清單裡查。

把金鑰放進查詢參數是不是更方便?

方便,而且會進伺服器日誌。?id= 定位密文可以;?key= 等於把解密能力交給託管方和沿途日誌。需要接收方能解密、託管方不能解密時,金鑰放 # 後面。

核對這些需要註冊嗎?原文會不會被分析腳本收走?

不需要註冊。分析若只記頁面類型或去掉 hash 後的路徑,不會帶走輸入框內容;若把完整 href 回傳,井字號後的金鑰可能進統計。打開 Network 看分析請求的 query,比閱讀「我們重視隱私」更直接。