Burn-Link ·

把閱後即焚連結貼進會產生預覽的聊天之後,閱讀次數為什麼不一定已經被用掉

Slack 頻道或 LINE 群組裡先跳出一張預覽卡片,不等於接收方已經讀過密文。聊天軟體為了產生連結預覽,會向頁面發一次 HTTP GET;這次抓取通常只讀標題和摘要,不會點「開啟並檢視」,也不會帶走位址列 # 後面的金鑰。下面把「產生卡片」和「消耗一次閱讀」拆開,並寫出當場能對照的步驟。不是閱後即焚建立頁的操作說明書。

對照預覽抓取 可當場核對 開啟即用
00 / 目錄

先分清 卡片抓的是哪一層

本篇只回答「貼進會產生預覽的聊天之後,閱讀次數為什麼不一定已經被用掉」。不是閱後即焚的表單說明,也不是「# 為什麼不進伺服器日誌」那一層。

01 / 抓取

預覽卡片抓的是 哪一層

卡片出現在你按下送出的前後幾秒。動作來自聊天軟體的伺服器,不是接收方的手指。

把一條 https://…/s.html?id=…#… 貼進 Slack、LINE、Discord 或 Microsoft Teams 之後,輸入框或訊息下方常常先跳出一張小卡片:標題、一兩句摘要、有時還有圖示。很多人把這張卡片讀成「連結已經被開啟過了」。順序其實相反。聊天軟體要先向這個位址發一次 HTTP GET,從回傳的 HTML 裡抽出 og:titleog:description 或普通 <title>,再畫卡片。接收方此時可能還沒點進去。

Slack 把這件事寫進了官方說明。預設情況下,使用者和 Slack 應用程式發出的、帶完整網址的訊息都會展開連結;媒體內容也可以展開。英文技術說明見 Slack 文件《Unfurling links in messages》;繁中產品說明則把同一行為叫做「連結預覽」,見 Slack 說明中心〈分享連結及設定預覽偏好設定〉。專門負責展開連結的機器人自稱 Slackbot-LinkExpanding 1.0 (+https://api.slack.com/robots)。同一頁 Slack Robots 寫明:它會盡量少取頁面(使用 HTTP Range),目標是 oEmbed、Twitter Card 和 Open Graph 標籤;如果標籤指向圖片、影片或音訊,才會再取那份檔案做校驗。回應大約快取 30 分鐘。文件沒有寫「執行頁面上的 JavaScript」或「替使用者點按鈕」。

瀏覽器發出這次 GET 時,# 後面的內容不會進入請求列。IETF RFC 3986 §3.5 把 fragment 定義成用戶端識別,不參與伺服器對 URI 的處理。所以預覽機器人即便完整複製了你在聊天框裡看到的字串,真正打到 UsePwd 的請求通常只到 s.html?id={編號}。金鑰為什麼不進存取日誌,見已有文章URL 的 # 片段為什麼不會出現在伺服器日誌裡。本篇要補的是另一層:這次不帶金鑰的 GET,算不算「讀過一次」。

先記一句

預覽要問的不是「聊天軟體有沒有看見這串網址」,而是「這一次 GET 有沒有走到取密文的介面」。卡片畫出來,只證明標題被讀過;密文還在不在,要另查。

02 / 分層

第一次 GET 為什麼 不等於讀過

若工具把「誰來取密文」和「誰來開啟落地頁」綁成同一次請求,預覽機器人就會把唯一的一次閱讀用掉。分層之後,這兩步不再是同一個動作。

不少一次性連結把密文直接掛在被預覽的那個 URL 上:機器人 GET 一次,伺服器就回傳正文並刪除紀錄。接收方隨後點開,看到的是「已被閱讀」或 404。這個失敗很常見,原因不是 Slack 或 LINE「惡意燒掉秘密」,而是實作把 RFC 裡本該安全、可快取的 GET,當成了「銷毀密文」的動作。

UsePwd 的閱後即焚把三步拆開。建立時,當前分頁用 Web Crypto 按 AES-256-GCM 加密,明文上限 32 KB;上傳欄位只有密文、過期時間和閱讀次數。過期時間可選 1 小時、24 小時、7 天,或「僅閱後焚毀」;閱讀次數 1–10,預設 1。連結形態是 s.html?id={id}#{key}。建立頁和閱讀頁都開啟即用,雙方都不用註冊。伺服器只暫存密文,看不到明文,也沒有帳號可以按使用者找回。

接收方開啟閱讀頁時,頁面先向 /api/secrets/{id}/status 詢問密文還在不在。這一步只回傳 activeburnedexpired,不增加閱讀計數,也不刪除紀錄。只有點了「開啟並檢視」,瀏覽器才會請求 /api/secrets/{id} 取回密文,並在本機用 # 後的金鑰解密。達到建立時設定的次數後,伺服器刪除密文;再取會得到 410。預覽機器人停在落地頁 HTML,常見情況下既沒有金鑰,也不會去點那個按鈕。

落地頁 GET
s.html?id=編號

回傳靜態閱讀頁。標題是「開啟閱後即焚連結」,不含明文。預覽卡片通常停在這一層。

狀態查詢
/secrets/{id}/status

只回答還在、已焚毀或已過期。反覆查詢也不會把次數加一。

取密文
/secrets/{id}

這才計入一次閱讀。預設 1 次後刪除密文。閱讀頁上對應「開啟並檢視」。

閱讀頁還寫了一句可以當場核對的提示:點按鈕「可能消耗一次閱讀次數」。沒有腳本就無法解密,頁面用 <noscript> 說明了這一點。預覽機器人若只讀 HTML、不執行腳本,連狀態查詢都不會發出;即便某家內建瀏覽器執行了腳本,也只先走到「密文還在」,仍要等人點按鈕。

03 / 機器人

哪些機器人會來、拿走什麼

能畫卡片的軟體很多。它們拿走的是頁面抬頭,不是 # 後面那串金鑰,更不是解密後的明文。

Slack 的連結展開機器人、Discord 用來產生嵌入卡片的抓取、LINE 把網址收成一張預覽的伺服端請求,以及 Teams 的類似展開,都屬於同一類:先 GET 頁面,再從 HTML 裡找標題和摘要。台灣站點實測時,LINE 的網址預覽常見 User-Agent 會帶 facebookexternalhitline-poker,對照見 黑暗執行緒〈美美的 FB/LINE 連結預覽是如何產生的?〉。這類抓取讀的是靜態標籤,不是替你按下「開啟並檢視」。

Slack 另外說明,它不把 robots.txt 當成傳統爬蟲規則來遵守,因為展開連結是「替發連結的人辦事」,不是順著站點亂爬。你在閱讀頁加 noindex,擋得住搜尋引擎收錄,擋不住頻道裡的那張卡片。UsePwd 的閱讀頁本身是臨時密文場景,帶 noindex, nofollow,也不進網站地圖。頁面標題固定寫成「開啟閱後即焚連結」,描述只說明解密在當前分頁完成、金鑰在 # 後面。預覽卡片若成功產生,頻道或群組裡看到的就是這句通用說明,不會出現你寫入的密碼、Token 或工單原文。這和「伺服器日誌裡有沒有金鑰」是兩件能同時成立的事:卡片證明有人 GET 過落地頁;日誌裡仍然沒有 fragment。

Slack 寫過,同一 URL 的展開結果大約快取 30 分鐘。短時間裡把同一條測試連結反覆貼進頻道,不一定會看到第二次抓取。要核對「機器人來過沒有」,應看你自己的存取日誌裡有沒有 Slackbot-LinkExpandingDiscordbot 或含 line-poker 這類 User-Agent,而不是盯著卡片重新整理幾次。日誌裡出現對 s.html 的 GET,仍然不能推論「/api/secrets/{id} 已被呼叫」。

落地頁 HTML 01 預覽常取這一層。標題通用,不含明文,也不帶 # 金鑰。
狀態介面 02 只有執行了頁面腳本才會問。問了也不消耗次數。
取密文介面 03 對應「開啟並檢視」。普通卡片抓取到不了這一層。
聊天紀錄裡的完整網址 04 這是另一份副本。次數沒掉,不等於連結只存在於接收方腦中。
04 / 會燒掉

什麼情況下次數 真的會被用掉

「不一定用掉」不是「永遠用不掉」。能點到「開啟並檢視」的存取,才會走進取密文那一層。

最常見的消耗者是接收方本人:開啟完整連結,看到「密文還在」,再點「開啟並檢視」。預設次數是 1。這一次成功之後,伺服器刪除密文;同一條連結再開啟會提示已被焚毀。若建立時把次數設到 2 或更高,前幾次取回後密文仍在,直到計數達到上限。過期時間到了也會刪除:預設 24 小時,也可以是 1 小時、7 天;選「僅閱後焚毀」則只按次數刪,不按鐘點刪。

第二類是會執行腳本並且模擬點擊的環境。部分郵件安全閘道、連結沙箱會在隔離瀏覽器裡開啟網址,有的還會點頁面上的主按鈕。那已經不是「產生一張卡片」,而是另一次完整的、像人一樣的存取。若沙箱點了「開啟並檢視」,次數會被用掉,接收方隨後看到焚毀態。這一層要用閘道自己的報告核對,不能從聊天卡片反推。

第三類是把取密文的介面位址直接發出去。閱讀頁位址是 s.html?id=;真正消耗次數的是 /api/secrets/{id}。一般使用者不會複製後一條。若有人把介面 URL 貼進會預覽的頻道,而那家工具又把第一次 GET 當成取密文,次數會在預覽階段被用掉。UsePwd 的分享形態不走這條位址,建立頁複製的是帶 # 的閱讀頁連結。

不要讀反

預覽沒燒掉次數,不等於秘密只存在於接收方那裡。聊天軟體仍保存完整網址,工作空間管理員還可能匯出紀錄。次數限制的是伺服器上密文的壽命,不是頻道或群組裡那串位址還能被誰開啟。

05 / 核對

當場怎麼 核對預覽

目標不是證明「全世界的聊天軟體都不會讀密文」,而是證明:卡片出現之後,取密文的介面還沒有被呼叫。

  1. 01
    準備一條不會用於真實交接的測試內容

    不要寫入正在使用的 API Key 或登入密碼。例如開啟閱後即焚,輸入 PreviewCard-20260905,次數保持預設 1,過期保持 24 小時。頁面開啟即用。下面的步驟只為對照預覽和介面,不把這段字交給同事當正式金鑰。

  2. 02
    記下完整連結,先自己開啟閱讀頁

    產生後應看到 s.html?id=# 兩段。用同一條連結在當前瀏覽器開啟。頁面應停在「密文還在」,並出現「開啟並檢視」。先不要點按鈕。按 F12 開啟開發者工具的網路面板,應能看到對 /api/secrets/{id}/status 的請求;此時不應出現對 /api/secrets/{id} 且無 /status 後綴的取密文請求。

  3. 03
    把同一條連結貼進會產生預覽的測試頻道

    用一個只有你自己的 Slack 頻道、LINE 測試群或 Discord 私人訊息。貼出後等卡片出現。卡片標題應接近「開啟閱後即焚連結」,不應出現 PreviewCard-20260905。回到閱讀頁重新整理,或換一個未點過按鈕的視窗再開啟同一連結,應仍停在「密文還在」。若這裡已經變成「已被焚毀」,說明有別的存取走到了取密文介面,而不是「卡片本身等於閱讀」。

  4. 04
    需要時再對照存取日誌裡的 User-Agent

    若你能看到站點存取日誌,預覽階段常見的是對 /tw/s.html?id=… 的 GET,User-Agent 可能含 Slackbot-LinkExpandingDiscordbotline-poker。同一時間不應出現對 /api/secrets/{id} 的成功取回。Slack 對同一 URL 大約快取 30 分鐘,短時間重複貼上不一定產生第二次抓取。

  5. 05
    最後才點「開啟並檢視」,確認次數被用掉

    現在點按鈕。網路面板應出現取密文的請求,頁面展示測試原文。關閉後再開啟同一連結,應提示已被焚毀。這一步用來對照「按鈕之後」和「卡片之後」不是同一狀態。測完即可丟掉這條連結,不要把它改派給真實接收方。

若你只是要把新金鑰交給另一個人,不要先把明文貼進頻道「試一張卡片」。測試內容和正式內容分開。正式交接仍然走閱後即焚;環境變數外洩後的第二輪風險,見雲端平台環境變數被拖走之後,新金鑰為什麼不該再發到聊天視窗。那篇寫的是「不要把 API Key 本身貼進可搜尋的紀錄」;本篇寫的是「連結貼進去之後,預覽抓取停在哪一層」。

06 / 邊界

次數沒掉,聊天裡還 留下什麼

把「卡片沒有燒掉密文」讀成「這條連結已經從頻道裡消失」,會漏掉幾條同樣能核對的邊界。

聊天軟體保存的是你貼出去的整串位址。誰能搜尋歷史、匯出工作空間、開啟備份,誰就能再次開啟閱讀頁。次數還在時,後開啟的人仍可能點「開啟並檢視」並讀到明文;次數用盡後,紀錄裡留下的是一條已焚毀的連結,加上當時的卡片標題。管理員匯出改不了伺服器上已經刪除的密文,但改不掉「當時頻道裡出現過完整網址」這件事。

截圖和轉發是另一份副本。有人把閱讀成功後的明文截下來,或把連結轉到另一個會預覽的群,邊界就從「這一次 GET」變成「下一跳還經過誰」。閱後即焚限制伺服器上密文的壽命和次數,不限制接收方是否截圖。UsePwd 沒有帳號和密碼庫,也不能按使用者找回丟失的連結。需要長期保存或多裝置同步時,應使用專門的密碼管理員。身分說明見關於

檔案是第三條路徑。單檔不超過 5 GB 的本機備份,走檔案加密盒,輸出 .lock / .enc,檔案預設不上傳。口令本身用密碼產生器在 6–128 字元之間產生,再用密碼檢測看強度。這些頁面和本篇一樣開啟即用。不要把「預覽沒燒掉次數」理解成「貼進任意頻道都安全」:聊天紀錄和預覽抓取不是同一層。

07 / 問答

預覽之後 常被問到的

下面四條只回答本篇的邊界,不重複建立頁上的按鈕說明。

看到卡片是不是已經讀過了 01 通常不是。卡片只說明落地頁被 GET 過。次數要等有人點「開啟並檢視」。
LINE 預覽會不會執行腳本 02 產生卡片的伺服端抓取通常只讀 HTML。對方點進內建瀏覽器才執行腳本,且仍要再點按鈕才會取密文。
# 後面的金鑰預覽機器人看得到嗎 03 HTTP 請求帶不走片段。機器人按網址去抓時,通常只帶到問號後的編號。
建立和閱讀要註冊嗎 04 不要。開啟即用。頂欄右側只有語言切換,沒有登入或密碼庫入口。
08 / 下一步

讀完去 核對一張預覽卡片

文章回答「預覽之後次數為什麼不一定已經被用掉」。要當場產生一條不用於真實交接的測試連結再貼進測試頻道,開啟閱後即焚即可,不必先註冊。