先記一句能核對的結論:開啟開發者工具 Network,對照網址列整串和文件請求的請求行。網址列可以有 # 後面的金鑰,請求行裡不該出現同一段。出現了,就不是「瀏覽器預設行為」,而是實作把 fragment 寫進了 query,或指令碼主動讀出後塞進了請求。
問號和井號不是同一層保護
一次性密文連結常被寫成「金鑰在 URL 裡」。這句話太粗,會把兩件完全不同的事疊在一起。URL 可以同時帶查詢和片段:問號 ? 打開查詢字串,井號 # 打開片段識別符。兩者都出現在網址列,接收方複製時也常常整串帶走,但對 HTTP 的待遇不一樣。
查詢會成為請求目標的一部分。你開啟 s.html?id=abc123,伺服器、反向代理、CDN 和存取紀錄依設計都能看到 id=abc123。片段預設不進入這次文件請求。你開啟 s.html?id=abc123#金鑰,瀏覽器向伺服器要的是帶 id 的那一頁,井號之後留在目前分頁,給頁面指令碼用 location.hash 讀取。
所以「金鑰在 URL 裡」只回答了人能不能看見整串,沒有回答伺服器能不能看見金鑰。適合放金鑰的是井號後面那一段,前提是實作真的把它留在 fragment,而不是圖省事寫進 query。
規範怎麼寫:fragment 留給用戶端
這不是某一個產品的私有約定。RFC 3986 第 3.5 節把 fragment 定義成「次級資源」識別:井號出現後一直到 URI 結束。片段怎麼解釋,取決於取回來的文件類型,由用戶端處理,不由 URI 方案改寫。
RFC 9110 第 7.1 節把這條規則接到 HTTP 上:瀏覽器解析出的目標 URI 不含 fragment,因為片段識別留給用戶端處理。送出文件請求時,請求行裡不應出現井號及之後的內容。
Referer 也按同一原則剝離。RFC 9110 寫明,使用者代理產生 Referer 時不得帶上 fragment 和 userinfo。MDN 對 Referer 的說明與此一致:標頭裡可以有來源、路徑和查詢,不可以有 # 片段。W3C Referrer Policy 在「把 URL 收成 referrer」的步驟裡,會先把 fragment 置空,再按策略決定還留不留路徑和查詢。
這三份材料合在一起,只證明一件事:按規範實作的瀏覽器,不會把井號後面的金鑰寫進 HTTP 請求行或 Referer。它不證明頁面指令碼、擴充功能或你貼到別處的文字也會自動刪掉這一段。
金鑰若放進問號,紀錄裡就是明文
把金鑰寫成 ?id=abc123&key=...,實作更簡單:伺服器用同一套 query 就能取密文、再代為解密。對「圖省事的線上加密頁」這很常見,對「託管端不該看見金鑰」則直接相反。
查詢會出現在請求行,也會進入大多數存取紀錄、反向代理日誌和部分 CDN 報表。金鑰一旦進 query,所謂「伺服器只存密文」在紀錄這一層就不成立:維運打開當天的 access log,就能看到解密所需的第二半。
有人會改成 POST body 傳金鑰。那能避開 URL 紀錄,但金鑰仍然離開了瀏覽器,到達了你聲稱「零知識」的那台機器。fragment 的價值恰恰是:金鑰根本不必作為 HTTP 欄位送出去,頁面在本機讀 location.hash 即可。
| 放哪裡 | 人能不能看見 | 這次 HTTP 能不能看見 |
|---|---|---|
| 問號後的 query | 能,在網址列和複製文字裡 | 能。請求行、代理和存取紀錄都會記 |
| 井號後的 fragment | 能,在網址列和複製文字裡 | 預設不能。請求行和 Referer 依規範不含這一段 |
| POST 欄位 | 網址列沒有 | 能。請求體到達伺服器 |
| 只在本機記憶體、從不寫入 URL | 對方看不到,除非另建安全通道 | 不能。但一次性連結也無法靠「開啟即用」完成交接 |
正確拆法:定位走問號,金鑰走井號
一次性密文連結要同時完成兩件事:告訴伺服器「取哪一條密文」,以及告訴接收方瀏覽器「用哪把金鑰在本機解密」。這兩件事不該走同一條 HTTP 欄位。
可核對的拆法是:s.html?id={id}#{key}。問號只帶定位用的 id,井號只帶金鑰。建立時,瀏覽器用 Web Crypto 做 AES-256-GCM,出站只允許密文;閱讀時,指令碼讀 location.hash,再向伺服器要密文、在本機解密。伺服器依設計只暫存密文,閱後焚毀。
MakePwd 的閱後即焚按這個邊界實作,建立頁和閱讀頁都開啟即用,沒有帳號。閱讀頁對接收方公開,不要求登入。這仍然只是實作選擇,不是「井號等於加密協定」。fragment 本身不提供機密性,它只是避免金鑰作為 HTTP 欄位離開分頁。
不要用真實口令、證件字號或未遮罩表格做實驗。準備一段可以丟棄的測試句,金鑰用這次才產生的一次性連結。核對的是請求行和網址列,不是把秘密再傳播一遍。
用 Network 和網址列當場核對
上一篇寫過如何用金絲雀檢查「明文有沒有當業務資料離開本機」。這一篇把範圍收窄:只核「金鑰有沒有進 HTTP」。步驟可以單獨做完,不必先讀完那一篇。
先建立一條無害測試文本,複製產生的完整連結。看清楚井號在哪:前面是 s.html?id=...,後面才是金鑰。然後換一個乾淨的分頁,開啟開發者工具,勾選 Preserve log,再貼上開啟。
- 對照網址列和文件請求的請求行。網址列應保留
#及之後的內容;文件請求的 URL 只應看到 path 和?id=,不應出現井號後的金鑰。 - 再看後續 XHR / Fetch。取密文的介面依設計可以帶
id,請求體或查詢裡不應再出現同一段金鑰。建立介面的 body 應是密文,不是你剛輸入的測試句。 - 單獨開啟造訪統計的 query 與 body。頁面路徑可以出現;
location.href若被整串寫入,金鑰會從「不進 HTTP 預設行為」變成「指令碼主動回報」。這是實作問題,不是規範失效。
三次都乾淨,只能支持很窄的結論:在你使用的這個瀏覽器、這一次開啟裡,金鑰沒有作為已觀察到的 HTTP 欄位離開分頁。換瀏覽器、換版本、或頁面改了指令碼之後,需要重新做。
這一層保護在什麼時候失效
fragment 擋的是「這次 HTTP 把金鑰送給託管端」。下面這些情境裡,金鑰本來就不靠 HTTP 預設行為保密,規範幫不上忙。
第一,完整連結被貼進 LINE、郵件或客服工單。接收端看到的是人眼可見的整串,井號後面一起被存進歷史紀錄。有的用戶端會丟掉 hash,只預覽問號前面的部分——那時接收方開啟後缺金鑰,閱讀頁應提示缺金鑰,而不是向伺服器再要一把。無論哪種,你已經把「持有即用」的憑證交給了另一個系統。
第二,頁面指令碼和擴充功能能讀 location.hash。這是閱讀頁能工作的原因,也是 XSS 或惡意外掛能拿走金鑰的原因。RFC 9110 第 17.11 節提醒過:片段不會進請求,但仍對使用者代理、擴充功能和回應帶來的指令碼可見。重新導向若繼承原 URL 的 fragment,還可能把本站片段帶到另一個網站。
第三,瀏覽器歷史、螢幕分享和剪貼簿。網址列裡的整串會出現在本機歷史裡;你把分頁投到會議室,井號後面同樣在螢幕上。這些都不經過伺服器,也不能用「fragment 不進 HTTP」來反駁。
第四,會改寫 URL 的轉址頁或短網址服務。中間頁如果只轉發 path 和 query,接收方開啟時 hash 已經沒了;如果它先在自己的頁面用 JavaScript 讀完整 href 再跳轉,金鑰就進了中間方的前端。短網址尤其要當場看:你交給對方的那一截,還是不是帶 # 的原串。
三個容易說反的地方
「井號後面更安全,所以整串可以隨便轉傳。」不成立。對伺服器更安全,對群組聊天紀錄不一定。完整 URL 是憑證,誰拿到誰就能開啟閱讀頁解密。
「Referer 可能把金鑰漏給外連。」按現行規範,瀏覽器產生 Referer 時必須去掉 fragment。真正要防的是頁面自己把 location.href 寫進分析、紀錄或第三方指令碼。核對方法仍然是看 Network 的回報內容,而不是假設「有外連就一定漏金鑰」。
「閱讀頁應當先登入,否則誰都可以開啟。」這把「誰持有完整連結」和「誰擁有帳號」混在一起了。一次性密文連結的存取控制就是整串本身。給接收方加登入門檻,並不能讓伺服器看不見金鑰——金鑰本來就不該傳給伺服器。MakePwd 的閱讀頁對接收方公開,建立與閱讀都無需帳號。
常見問題
井號後面會傳給伺服器嗎?
預設不會。RFC 9110 寫明目標 URI 不含 fragment。開啟 Network,文件請求的請求行裡不應出現 # 及之後的金鑰。伺服器依設計只看到路徑和 ?id=。
為什麼不把金鑰放在問號後面?
問號後的查詢會寫進 HTTP 請求行,託管端和存取紀錄都能看見。金鑰一旦進 query,「只存密文」在紀錄這一層就不成立。定位用 id 可以走問號,金鑰應走井號。
把完整連結貼進 LINE 還安全嗎?
對伺服器仍然看不到金鑰;對聊天紀錄、工單和瀏覽器歷史不再成立。完整 URL 持有即用。需要交接時,應確認對方拿到的是帶井號的原串,並清楚這串會出現在對方的歷史裡。
開啟閱讀頁需要登入嗎?
不需要。閱讀頁對接收方公開:問號裡的 id 用來取密文,井號裡的金鑰在本機解密。MakePwd 沒有帳號和密碼庫,建立與閱讀都開啟即用。
下次傳一次性秘密時記住的三件事
第一,看符號,不看「金鑰在 URL 裡」這句空話。問號進 HTTP,井號預設不進。第二,建立時確認出站的是密文,閱讀時確認請求行沒有 # 後面那一段。第三,把完整連結交給對方之前,先問自己:這串會不會進 LINE 紀錄、工單或會丟掉 hash 的短網址。後兩處失敗,與伺服器是否零知識無關。
若你還要核對「明文有沒有當業務資料離開分頁」,那是上一篇的範圍:用金絲雀搜 Network 的請求體和分析回報。本文只把「金鑰為什麼可以放在井號後面」收成可以對照規範和請求行的判斷。