跳转至

隐形字符检测

为什么文字里会有看不见的东西

文件外流追踪

机构要发一份文件给二十个人,于是准备二十份副本,每一份在不同的位置插入不同组合的零宽字符。内容看起来完全一样,打印出来也一样,复制粘贴到别的地方那些字符还是跟着走。文件外流之后,比对一下就知道是哪一份流出去的。

要转发手上的文件之前,先扫一遍。

钓鱼网址

аpple.comapple.com 看起来一样,前者的第一个字母是西里尔文的 а。域名注册商对混用字母的防范不一致,而人眼分辨不出来。

代码里的方向覆写

在源代码里放一个右至左覆写字符,人在编辑器里看到的顺序跟编译器读到的可以完全不同。注释看起来包住了一段代码,实际上没有。这类手法有个名字叫 Trojan Source。

几个真的会遇到的场景

记者收到爆料之后的查证动作

记者取得一份内部文件,向该机构查证是专业要求。查证时最自然的动作是出示文件,或引用其中一段精确的文字问「这一段是不是真的」。

如果文件是二十份带不同标记的副本之一,出示或引用查证等于把消息来源交给正在找他的人。每一步都做得很专业,来源却在查证那一刻被烧掉。

处理顺序:

  1. 收到文件先扫,别急着读完
  2. 扫出东西就先清干净,留一份原始档另外收好
  3. 查证时转述内容,不要出示文件本身
  4. 需要引用精确段落时重新打字,不要复制粘贴

第四步最容易被忽略。零宽字符跟着复制粘贴走,你把那一段贴进查证邮件里的同时也把标记贴了进去。

反方向也成立。媒体内部发给编辑台的稿子、公关公司发给记者群的禁运稿,同样可能带标记,用来确认谁提前放了出去。收到禁运稿的人,一样可能被同一招追出是谁提前外流。

场景的完整脉络见记者保护消息来源

组织之间流转的文件

联合声明的草稿在几个团体之间来回、倡议联盟的内部备忘、公部门发给咨询委员的参考资料。

这几种文件的共同点是会被转发很多次,而每一次转发都完整保留原本的不可见字符。第五手收到的人不知道第一手那份带了什么,也不知道自己转出去之后会被追到谁。

转给合作伙伴之前扫一遍,成本十秒。

收到的 bridge line 与 onion 地址

有人在聊天室贴给你一行 obfs4 bridge,或公告里放了一个 onion 地址。这两种都是长串、人眼记不住、只能复制粘贴的东西,正好是夹带隐形字符最方便的地方。

onion 地址用的是 base32,只有 az27。任何西里尔字母或不可见字符都不在那个集合里,贴进 Tor Browser 多半是连不上,而不是连到别的地方。bridge line 的情况比较实在:一长串设置里夹了不可见字符,会让解析出错,或在转贴过程中被截断成另一组看起来合理的参数。

真正会「连到别的地方」的手法不在检测范围里:攻击者可以自己算出一个前缀看起来很像的合法 onion 地址,那里面没有任何隐形字符,每一个字都是合法的 base32。分辨它只能逐字比对完整的 56 个字符,或从你信任的来源重新取得。

截图比复制粘贴干净,代价要算清楚

零宽字符跟着复制粘贴走,不会跟着截图走。所以要转传一段来源不明的文字时,截图比复制粘贴干净。

代价有三个,都要算进去:

  • 截图丢掉可搜索性,收到的人不能搜、不能复制其中一段去查证
  • 屏幕阅读器读不到图里的文字,是无障碍上的实质倒退
  • 截图本身带 metadata,而且很容易连通知栏、其他对话窗口、桌面上的文件名一起拍进去

第三项可以处理:用照片 metadata 清除器清掉 EXIF,并且截图前先看一眼画面上还有什么。前两项处理不掉,只能看场景取舍。

转给一个人看的敏感内容,截图合理。要公开发布、要让人搜索得到的内容,重新打字比截图好。

藏在文字里的 LLM 指令

不可见字符可以把一整段指令藏进看起来完全正常的文件、GitHub issue 或 commit message 里。人读不到,模型读得到。

标签字符区段特别适合藏这种指令:它能把英文一个字符对一个字符编成完全不显示的内容,长度不限,整段指令都塞得进去。检测会把标签字符标成可疑,因为正常文字用不到它们。

为什么一般人也该在意:你把一段来源不明的文字贴给 AI 助理,藏在里面的指令会被模型当成输入的一部分。那段指令可以要求模型忽略前面的话、改变摘要的立场、或在回答里插入特定链接。你看到的是一段普通文字,模型看到的是一段普通文字加一段指令。

反方向也有人在做:有些工具用零宽字符在 AI 生成的文字里打水印,用来事后判断某段文字是不是模型写的。同一种字符,一边拿来下指令,一边拿来做标记。

扫过不能证明什么

如果你用完这个工具的结论是「扫过了,可以安心转发」,那它让你比不用更危险。

零宽字符是最粗糙的一种标记方式。正因为粗糙,才容易检测。真正要追人的一方有更多不在检测范围里的做法:

  • 每一份副本替换几个同义词,「立即」与「马上」、「大约」与「约莫」
  • 调整段落之间的空白行数、换行的位置
  • 微调标点,这里用逗号那里用顿号
  • PDF 里调整个别字符之间的间距,肉眼看不出,文件里是不同的数值
  • 页面上放肉眼难辨的视觉标记,例如某个字的颜色差一阶

还有完全不在文字里的。彩色激光打印机会在每一张打印出来的纸上留下黄点数组,编码打印机序号与打印时间,这套机制叫 Machine Identification Code。文件经过打印再扫描之后,你手上那份的来源仍然查得到。

检测结果能证明的是「有」,不能证明「没有」

扫出东西表示这份文件确实被标记过。扫不出东西只表示没有用这几种方式标记,不表示干净。

处理真正敏感的文件时,正确的做法是重新打字或转述内容,不要传递原始文件。

教检测,不教植入

同一套手法,两种用途。机构要保护内部文件不外流是合理的需求,同一组零宽字符用来找出吹哨者就是压迫工具。技术本身分不出这两者,使用它的人分得出来。

社群的立场是:说明手法怎么运作,让收到文件的人有能力自保,但不提供产生带标记副本的功能。

不做的具体包括:把一段文字加上不可见标记的生成器、批量产出多份带不同标记副本的工具。

「载入示范文字」给的是固定的一段,不能换成你自己的文字。那一段里有两个零宽空格、一个冒充网址的西里尔字母、四个标签字符,让你看得到检测结果长什么样。示范的教育效果到此为止,没办法拿去标记真实文件。想更完整验证检测范围的人,用测试档里的案例(tools/test_invisible.mjs)。

立场写在这里,方便日后讨论引用。

用 Tor Browser 查可疑内容的话

Tor Browser 的安全等级调到 Safest 会关掉 JavaScript,检测功能就会停摆,而收到来路不明的消息正是那篇文章建议把等级调高的场合。

把可疑的网站与检测分开处理:在高安全等级下复制那段文字,切回 Standard 贴进来扫,扫完再调回去。扫描在你的设备上做,不连外。

误判是这个工具最大的风险

不是所有隐形字符都有问题。

👨‍👩‍👧 这个 emoji 是三个 emoji 用两个零宽连字接起来的,那些连字非有不可。变体选择器是 emoji 的一部分。阿拉伯文与希伯来文本来就需要方向标记。整段俄文里的 а 就只是俄文。

全部报成可疑的话,警示会失去意义,看几次之后就没有人看了。所以它分两级:橙色是不必看语境就可疑的,灰色是要看前后才知道的。emoji 里的连字、有 RTL(从右到左书写)文字时的方向标记,都不会被报成可疑。

测试里有一整组案例守着不该报的别报,跟「抓得到」那组一样多。

清理

两个按钮。一个只清可疑字符,emoji 保持完整。另一个清掉所有看不见的字符,会把 emoji 家族拆散,要的是一份绝对干净的文字就选第二个。

同形字不会被自动换掉。那是可见的字,换掉会改变原意,而你可能正在处理一段真的俄文。工具只标出位置与它长得像哪个拉丁字母,换不换由你决定。

离线可用

跟这一区其他工具一样,程序存进设备之后没有网络也能用。你贴进来的可能正是不想外流的东西,所以检测没有任何网络请求,也不把文字写进任何存储,断网时照样可以用。

要把这一页带着走,见离线阅读