文字隱寫術是將一則秘密訊息藏在一段看似普通的文字內部的做法,如此一來,任何瞥見掩護句子的旁人都無法察覺文件中藏有任何額外內容。文字隱寫術所使用的訣竅,是在一段原本正常的句子中插入零寬度的 Unicode 字元;這些字元在一般字型下沒有寬度,因此句子在螢幕上讀起來仍然一樣。在幕後,隱藏訊息的每個 UTF-8 位元組會被展開成剛好八個不可見的位元,一個明確的起始與結束標記會框住承載內容,接著這個被框住的序列會被塞進掩護文字第一個可見碼點之後。解碼模式會定位這些標記,確認位元數為八的倍數,驗證還原後的位元組是格式正確的 UTF-8,然後同時回傳還原出的秘密訊息與恢復的可見掩護文字。所有編碼與解碼作業都在瀏覽器本地端進行,因此無論是掩護文字或秘密訊息都不會離開你的機器;這個工具採用透明且可還原的慣例,而不是宣稱能與所有其他隱寫術網站相容。

text steganography explained
文字隱寫術解析:零寬度 Unicode 基礎

「文字隱寫術」的真正含義

隱寫術是在一個載體中隱藏另一則訊息的更廣泛技術,目的是讓這個隱藏通道的存在不顯而易見。一個歷史上的例子是用檸檬汁寫字,再用熱度讓字跡顯現;紙張看起來仍然是一片空白。在資訊技術領域中,載體通常是一個數位物件,例如圖片、音訊檔、影片,或在文字隱寫術的情境中,就是一段看似正常句子的字元。其目標是隱蔽,而不是機密性:隨意的讀者根本不應懷疑內容中有任何異狀,這也是隱寫術常被形容為「在光天化日之下隱藏」的原因。

文字隱寫術是最困難的一種變體,因為文字幾乎沒有視覺上的餘裕空間。你無法像在 JPEG 中那樣巧妙地微調像素值,也沒有明顯的頻段可以埋入訊號。取而代之的是,你必須把位元藏在預設情況下會被算繪引擎忽略的碼點中。現代 Unicode 保留了少數幾個「預設可忽略」的碼點,正是為了這類用途:它們參與字元串流,但在一般排版中不產生字形也沒有可測量的寬度。這個特性使它們成為在不更動讀者所見內容的情況下,承載秘密位元流的自然位置。

零寬度 Unicode 如何承載隱藏訊息

文字隱寫術使用一套明確、有文件記載的慣例,讓任何讀者都能審核這項轉換過程。對應表僅包含四個碼點,每個都有明確的角色:

角色碼點意義
起始標記U+2063開啟被框住承載內容的不可見分隔符
結束標記U+2064關閉被框住承載內容的不可見加號
位元值 0U+200B零寬度空格
位元值 1U+200C零寬度不連字

每個隱藏的 UTF-8 位元組會以八個位元表示,最高位元優先,因此線上傳輸的順序符合讀者所預期的位元組順序。因此,一則簡短的隱藏訊息就是一長串交替出現的零寬度空格與零寬度不連字,夾在兩個不可見標記之間。整個被框住的序列會插入掩護文字第一個可見碼點之後,因此即便底層的碼點數量大幅膨脹,句子的結構仍得以保留。根據 Unicode 協會的Unicode 安全考量報告,預設可忽略的碼點可能在處理過程中被悄悄移除,而這正是本工具會警告的失敗模式。

在掩護句子中隱藏訊息

隱藏流程會將一段普通句子與一則獨立的秘密訊息,合併成一條隱寫字串,讓你可以在任何能忠實往返 Unicode 的地方貼上使用。

  1. 開啟文字隱寫術工具,並切換到隱藏模式。
  2. 在可見文字欄位中輸入一段普通的掩護句子,例如「The meeting is on Thursday.」。
  3. 在秘密欄位中輸入一則簡短的隱藏訊息。
  4. 點擊產生,把每個位元組八位元的位元流嵌入掩護文字中,並以 U+2063 與 U+2064 框住。
  5. 透過一個會保留預設可忽略碼點的管道,複製產生的字串。
  6. 在傳送至其他地方之前,先在第二個視窗中解碼複製的字串,以確認隱藏訊息與可見掩護文字都成功完成往返。

一個具體的演算範例:UTF-8 中的隱藏訊息「Hi」是位元組 0x48 與 0x69。0x48 以最高位元優先的二進位表示為 01001000;0x69 則是 01101001。每個 0 會變成一個 U+200B,每個 1 變成一個 U+200C,因此標記內的承載內容是依 0-1-0-0-1-0-0-0-0-1-1-0-1-0-0-1 順序排列的十六個不可見碼點,以 U+2063 與 U+2064 框住,並插入掩護句子的第一個字元之後。可見的句子維持不變;底層的序列則變得長得多。

解碼訊息並確認掩護文字的往返

解碼模式是整個工作流程中的驗證環節,其重要性不亞於隱藏模式,因為傳輸過程通常是隱寫術最容易出錯的地方。

  1. 將精確的隱寫字串貼到解碼欄位,不要重新輸入,才不會在過程中遺漏或重新編碼任何字元。
  2. 執行解碼;工具會定位 U+2063/U+2064 框,驗證每個承載字元都是兩種位元符號之一,檢查位元數是八的倍數,並把產生的位元組以嚴格的 UTF-8 解碼。
  3. 讀取還原出的隱藏訊息與還原出的可見掩護文字;兩者都應與原始內容逐位元組相符。
  4. 若解碼失敗,最可能的原因是你的傳輸管道移除了預設可忽略的碼點;透過開發者檢視原始碼檢視器複製原始碼點,並與原檔比對,找出是哪個平台破壞了字串。

此工具只會取出第一個有效的框選區段,並把格式錯誤的框視為錯誤而不是猜測,因此損壞的承載內容會大聲地失敗,而不是被解碼成一則誤導性的訊息。這正是一個傾向誠實回報「無法解碼」,而非給出貌似合理卻錯誤答案的工具應有的行為。

為什麼隱藏訊息會在傳輸過程中消失

文字隱寫術仰賴每一個不可見碼點都能從傳送方存活到接收方。許多日常使用的平台並不會保留這些碼點。電子郵件閘道可能會正規化換行字元;聊天客戶端與內容管理系統常常會清理貼上的內容,移除預設可忽略的字元;社群網路在清除非列印碼點方面尤其積極。剪貼簿的重新編碼器、自動校正、搜尋索引器以及安全掃描器,各自都可能從不可見的位元流中咬掉幾個字元。只要有一個位元消失,承載內容就可能變得格式錯誤,或被解碼成完全不同的位元組序列;讀者要嘛會看到錯誤,更糟的是,看到一則看似合理卻錯誤的訊息。

實際的規則是,螢幕截圖與列印副本根本無法承載隱藏資料,因為載體是 Unicode 字元本身,而不是算繪出來的像素。你應該只透過已端對端測試過的管道來傳送隱寫字串。會精確保留 Unicode 的純文字編輯器,比那些把貼上內容當成 HTML 處理、移除控制字元,或自行插入隱藏格式的所見即所得編輯器,更有可能保留承載內容。若有疑慮,在依賴該次傳送之前,請先在接收端把輸出貼入解碼模式。

限制、容量,以及本工具不會做的事

隱藏訊息的長度上限為 10,000 個 UTF-8 位元組,掩護文字的上限為 100,000 個碼點。這些上限存在的原因在於,每個隱藏的位元組會額外產生八個碼點加上框選標記,而無限制的成長會讓 DOM 失去回應能力。由於表情符號與非拉丁文字元每個可能佔用數個 UTF-8 位元組,因此秘密訊息的可見字元數可能小於位元組上限。掩護文字不得已含有本慣例的起始或結束標記,因為接受它會讓承載內容的界線變得模糊;位於框選區域外的普通零寬度字元仍屬於可見掩護文字的一部分,不會被解讀為位元。

文字隱寫術並不提供機密性、真實性或完整性。其對應方式有文件記載且易於偵測,任何找到框選區段的人都能讀取訊息。字元計數器、差異比對工具、原始碼檢視器,或是具 Unicode 感知能力的安全掃描器,都能立即揭露隱藏的碼點;因此這個工具適合用於示範、學習 Unicode 如何承載非算繪資料,以及無害的謎題,但不適合用來隱藏憑證、有害的指令、個人資料,或任何會違反平台規定的內容。若要進行持久的資料交換,應優先使用可見的編碼方式或經身分驗證的檔案格式,並把隱寫術視為整套工具中的其中一個技巧,而不是單獨構成一道安全界線。

隱寫術與密碼學的比較

密碼學讓訊息對外人而言無法讀懂;隱寫術則讓訊息的存在變得不可見。這兩個概念解決的是不同的問題,而一般建議是在真正需要機密性時將兩者結合:先用經過審核的加密法加密秘密訊息,接著選擇性地以隱寫方式藏入密文。例如,你可以先用線上 AES 加密把敏感文字加密,再把產生的結果以文字隱寫術嵌入一段無害的掩護句子中。只能看到掩護文字的讀者無從得知秘密;即使察覺到隱藏位元流的讀者,也必須破解加密法才能讀取訊息。對於想要把這個慣例與其他方法比較,或嘗試命令列工作流程的讀者,零寬度 Unicode 方法指南以不同的格式說明相同的概念。

延伸閱讀:把文字轉成十六進位碼:挑選 UTF-8 輸出格式