隐写术 - 在文本中隐藏秘密信息的技术与字符数

约 7 分钟阅读

这段文字中隐藏着秘密信息 - 如果有人这样告诉你,你会在哪里寻找?每句话的首字母?特定字符之间的间距?还是可能嵌入了不可见的字符?隐写术(steganography)是隐藏信息存在本身的技术。如果说加密是让信息"无法阅读"的技术,那么隐写术就是"让人根本察觉不到信息存在"的技术。而这项技术有时可以通过字符计数这一简单行为来检测。

从古代延续至今的"隐藏"技术

隐写术的历史可以追溯到公元前 5 世纪的希腊。历史学家希罗多德记录的著名故事中,为了警告波斯帝国的入侵,将奴隶的头剃光,在头皮上刺上信息,等头发长出后再作为信使派出。这是终极的低带宽通信,信息到达需要数周时间。

中世纪欧洲广泛使用隐形墨水(柠檬汁、牛奶、尿液等)进行秘密通信。二战期间,德国间谍使用微点技术(将文件拍照缩小到极小尺寸伪装成句号)。这项能在一个句号中隐藏一整页文字的技术,被 FBI 局长胡佛称为"敌方间谍活动中最大的进步"。

基于文本的隐写术手法

数字时代的文本隐写术有几种代表性手法。

离合诗 - 隐藏在首字母中的信息

离合诗(acrostic)是将每行或每句的首字母连接起来就会出现秘密信息的手法。这是最古典的文本隐写术,自古以来就用于诗歌和歌词中。

著名的例子:2003 年加利福尼亚州检察长比尔·洛克耶尔给辞职的州议员写了一封信。将每段的首字母连接起来拼出"I FUCK YOU",引发了重大丑闻。离合诗可以在不增加字符数的情况下嵌入信息,但有意搜索时很容易被发现。

空白字符操作

通过操纵单词间空格数量来嵌入比特信息的手法。1 个空格表示"0",2 个空格表示"1",编码二进制数据。人眼难以察觉空格的微妙差异,但使用字符计数工具可以检测到"相对于可见单词数,空格过多"。

零宽字符隐写术 - 不可见字符的世界

现代文本隐写术中最强大的手法是利用零宽不可见字符Unicode 定义了多个在屏幕上不显示但作为字符数据存在的"零宽字符"。

Unicode 码位名称原始用途隐写术中的角色
U+200B零宽空格指定可换行位置表示比特"0"
U+200C零宽非连接符抑制连字表示比特"1"
U+200D零宽连接符促进连字附加比特值
U+FEFF零宽不换行空格(BOM)字节序标记分隔符

使用 U+200B 和 U+200C 两种零宽字符,可以用 2 值(0 和 1)表示 1 比特。8 个零宽字符构成 1 字节,即 1 个 ASCII 字符。隐藏 5 个字符的信息"Hello"需要 40 个零宽字符。

将这 40 个零宽字符分散嵌入普通文本的单词之间,外观完全不变。但用字符计数工具比较"可见字符数"和"实际字符数(字节数)",就会检测到不自然的差异。理解Unicode 基础知识就能确定这种差异的原因是零宽字符。

零宽字符隐写术的实现示例

来看具体的嵌入流程。考虑将秘密信息"Hi"嵌入普通文本"Good morning"的情况。

"H"的 ASCII 码是 72,二进制为 01001000。"i"是 105,二进制为 01101001。将 0 转换为 U+200B(零宽空格),1 转换为 U+200C(零宽非连接符),生成 16 个零宽字符串。

将这 16 个零宽字符插入"Good"和"morning"之间。外观仍然是"Good morning",但实际数据包含 16 个不可见字符。文本编辑器计数为 12 个字符,但程序计算 Unicode 码位数为 28 个。差值 16 个字符就是隐藏信息的真面目。

更高级的实现使用 3 种以上零宽字符进行三值以上编码,用更少的零宽字符表示相同信息。使用 U+200B、U+200C、U+200D 三种,每个字符约 1.58 比特(log₂3),可以用约 5 个零宽字符表示 8 比特的 ASCII 字符。

同形字攻击 - 外观相同的不同字符

同形字(homoglyph)是指外观几乎相同但 Unicode 码位不同的字符。例如,拉丁字母"a"(U+0061)和西里尔字母"а"(U+0430)在许多字体中外观完全相同。

拉丁字母码位西里尔字母码位外观差异
aU+0061аU+0430几乎相同
eU+0065еU+0435几乎相同
oU+006FоU+043E几乎相同
pU+0070рU+0440几乎相同
cU+0063сU+0441几乎相同

同形字攻击利用了这一特性。在钓鱼网站 URL 中将"apple.com"的"a"替换为西里尔字母"а",外观相同但会导向完全不同的域名。在隐写术中,将文本中特定字符替换为同形字可以嵌入比特信息。

检测同形字需要检查每个字符的 Unicode 码位。正如密码长度与安全性中提到的,外观相同但字节序列不同的情况构成严重的安全风险。

作为对策,主流浏览器限制了 IDN(国际化域名)的显示。当域名混合多种文字(拉丁字母和西里尔字母等)时,浏览器以 Punycode(以 xn-- 开头的编码格式)显示域名,警告用户这是假冒网站。Chrome 在 2017 年的版本 58 中引入了这一对策。

文本水印技术

作为隐写术的应用,存在文本数字水印技术。虽然图像和视频水印广为人知,但在文本中嵌入水印的技术也存在。

水印方法原理检测方法抗性
零宽字符嵌入用不可见字符存储比特信息字符计数复制粘贴时可能丢失
同义词替换"大的"→"巨大的"等同义词替换与原文比较对文本编辑有抗性
句法转换主动语态→被动语态等句法转换与原文比较对文本编辑有抗性
空白操作操纵空格和制表符数量空白字符统计分析格式变更时丢失

同义词替换水印在不改变文本含义的情况下嵌入比特信息。例如,将"大的"替换为"巨大的"表示 1 比特信息。这种方法可能改变字符数,但对文本编辑和复制粘贴有抗性。

加密与隐写术的区别

加密(encryption)和隐写术经常被混淆,但它们是根本不同的技术。

特性加密隐写术
目的使信息内容不可读隐藏信息的存在
可检测性密文的存在是明显的信息的存在本身未知
对字符数的影响与原文相当载体文本字符数可能增加
密钥需求解密需要密钥知道方法就可能提取
组合使用可单独使用与加密组合最强

最安全的方法是先加密信息,再用隐写术隐藏。即使隐写术被破解、信息的存在被发现,如果已加密则内容仍然不可读。

通过字符计数检测隐写术

检测基于文本的隐写术最简单的方法是字符计数。以下不自然的差异可作为检测线索。

可见字符数与实际字符数(码位数)不一致。嵌入零宽字符时,文本编辑器中可见的字符数少于程序计算的字符数。例如,外观为 100 个字符的文本实际包含 180 个字符的数据,则可能嵌入了 80 个零宽字符。

字符编码大小的不自然也是线索。纯 ASCII 文本(仅字母数字)在 UTF-8 中应为 1 字符 = 1 字节。但如果混入西里尔同形字,一些看起来是 ASCII 的字符变成 2 字节。如果总字节数超过字符数,应怀疑同形字的存在。

Twitter 的零宽字符对策与字符计数

Twitter(现 X)采用了独特的字符计数规则来防止利用零宽字符的隐写术和字符限制规避。Twitter 的字符计数库"twitter-text"将包括零宽字符在内的特定 Unicode 字符计入字符数。这意味着大量嵌入零宽字符会导致外观很短的文本也达到 280 字符限制。

这一对策不仅是为了防止隐写术,也是为了确保服务的公平使用。无限制嵌入零宽字符可能不当消耗数据库存储或导致时间线显示问题。

隐写术检测工具和技术

存在几种专门用于检测基于文本的隐写术的工具和技术。

检测方法目标原理局限性
字符数与字节数比较零宽字符可见字符数与实际字节数不一致难以区分合法零宽字符
Unicode 类别分析同形字验证文本中字符所属 Unicode 块的一致性多语言文本中误报多
统计分析空白操作验证空格分布是否符合自然语言统计短文本精度低
熵分析全般验证文本信息熵是否在自然语言范围内难以应对高级手法

最简单有效的检测方法是将文本复制粘贴为纯文本,比较与原文的字节数。如果包含零宽字符或同形字,字节数会有差异。字符计数工具如果能同时显示"可见字符数"和"Unicode 码位数",就能即时检测这种差异。

SNS 与隐写术 - 实际案例

2016 年,安全研究人员报告了 Twitter(现 X)上实际使用零宽字符隐写术的案例。推文外观是正常的 280 字符以内的文本,但包含零宽字符后实际数据量远大于此。

一些企业通过在内部文件中用零宽字符嵌入员工 ID 来实施"文件指纹"。如果机密文件泄露到外部,分析嵌入的零宽字符就能确定是谁泄露的。这种手法由于完全不改变文件外观,比传统水印更难检测。

隐写术是隐私保护和信息安全两方面都很重要的技术。在审查严格的国家,活动人士使用隐写术发布信息。另一方面,恐怖分子和犯罪分子也可能用它来隐藏通信。字符计数这一看似简单的工具,可以成为检测这些隐藏信息的第一道防线。

信息安全和密码技术的相关书籍也可以在 Amazon 上找到

分享这篇文章