指南 · 消息风险 · · Stefan Tsezarov · 更新于
你看不见的文字
两条消息可能看起来一模一样,却包含完全不同的字符。
你复制了一条消息。
它看起来很正常。
三句普通的话。没什么奇怪的。
你把它粘贴到文档、网站、钱包或 AI 助手中。
突然发生了一件事,而你看到的那条消息似乎从没说过这件事。
怎么会这样?
因为你在屏幕上看到的,未必就是文本里的全部。
有些字符根本不占可见的空间。
它们仍然在那里。
它们仍然可以被复制、粘贴、存储和处理。
你只是看不见它们。
你的电脑看字母的方式和你不同
你看一句话时,看到的是形状。
你的电脑看到的是数据。
例如字母 A,由一个具有特定码位的 Unicode 字符表示。空格是另一个字符。每个字母、数字、标点符号以及许多其他符号,都有自己的表示。
但并不是每个字符都会产生可见的形状。
有些字符告诉软件文本应该如何表现。
它们可以告诉程序:
- 把这些字符连起来
- 不要在这里换行
- 把这一段从右往左显示
- 改变这些字符的顺序
- 或者在某些情况下,只是占据一个位置而不绘制任何东西
所以你可以有两段看起来完全相同、却是不同字符序列的文本。
这种差别可能很重要。
非常重要。
值得了解的三种隐形文字
涉及的机制有好几种,但有三种特别有助于理解。
1. 不显示的字符
这些通常被称为零宽字符。
例子包括零宽空格(zero-width space)、零宽连接符(zero-width joiner)和词连接符(word joiner)。
它们在文本中占据一个位置,却不产生可见的像素。
例如,下面这两个词可能看起来一模一样:
payment
payment
第二个词里面有一个隐形字符。
你很可能看不见它。
你的电脑看得见。
这意味着这两个字符串其实并不相同。
这可能影响搜索、过滤器、用户名、标识符,以及其他比较底层字符而不是比较人眼所见内容的系统。
而其中一些字符的存在是有正当理由的。
我们稍后再谈这一点。
2. 改变你所见顺序的字符
有些隐形字符会影响文字方向。
这是必要的,因为阿拉伯语和希伯来语等语言从右往左书写,而英语从左往右书写。
同时包含两者的句子,可能需要一些指令来告诉软件如何显示不同的部分。
这些指令可能很有用。
但也可能被滥用。
一个方向控制字符可以让电脑存储的字符在屏幕上以不同的顺序显示。
这显然会带来安全问题。
例如,一个文件名可能看起来以某个扩展名结尾,而它底层的字符序列说的却是另一回事。
同样的原理在源代码中也可能很重要。
审阅者看到的是一样东西。
电脑处理的是另一样东西。
3. 可以携带隐形信息的字符
这是最不寻常的一种。
Unicode 中有一个被称为 Tags 区块的范围。
这些字符原本是为标记用途设计的,正常显示时通常是不可见的。
有趣的是,单个 Tags 字符可以表示普通的 ASCII 字符。
把足够多的这种字符放在一起,就能编码文本。
因此,一句话可以包含一串不会出现在屏幕上的额外字符。
在你看来,这条消息可能就是一句再普通不过的问候。
对于读取底层字符的软件来说,那里可能还有更多内容。
而当目的地是 AI 助手时,这种区别就变得格外有意思。
AI 并不是把你的消息当作屏幕截图来看。
它处理的是字符。
所以你看不见的东西,仍然可能是输入的一部分。
这是它的样子
看这句话:
Send the payment to the address below.
看起来很正常,对吧?
现在想象一下,其中被插入了隐形字符。
在你的屏幕上,它可能仍然是这个样子——而这一句就是真的,已经为你准备好用来测试:
Send the payment to the address below.
但在底层,电脑实际上可能保存着额外的字符。
例如:
你看到的:
Send the payment to the address below.
电脑收到的:
Send the 隐形 payment 隐形 … TAGS
可见的句子没有变。
底层的字符串变了。
这才是关键。
外观相同。数据不同。
如果你用扫描器检查这个特别准备的示例,即使你的眼睛看不见,扫描器也能识别出这些字符。
手动重新输入这句可见的话,那些隐藏字符就不会跟着过来。
整个花招就是这样。
不是那个字母的字母
隐形字符并不是唯一的问题。
有时字符完全可见。
只是它不是你以为的那个字符。
看看这两个:
savesavesave
sаvesavesave
它们看起来几乎无法区分。
但仔细看第二个。
第二个字符不是拉丁字母 a。
它是西里尔字母 а。
它们看起来很像。
它们是不同的字符。
这种手法被称为同形字攻击。
同样的思路可以用在用户名、域名、代币名称和其他标识符上。
假账户可以使用形似字符。
假网站可以使用仿冒域名。
对人来说,这个名字看起来可能很眼熟。
对电脑来说,它是一个完全不同的字符串。
这在哪些地方会真正造成问题?
这不只是一个奇怪的 Unicode 小把戏。
凡是文本会被复制、比较或据以行动的地方,它都很重要。
AI 助手
你把一条消息粘贴到 AI 助手中。
你看到的是一个问题。
底层文本可能包含你看不见的字符。
因为助手处理的是实际的文本,而不只是视觉上的外观,这些字符就成了输入的一部分。
这就是为什么在把不可信的文本粘贴到能采取行动或处理敏感信息的系统之前,做一次隐形字符检查可能很有用。
用户名和账户名
两个账户可能看起来名称相同,却包含不同的字符。
这可能让冒充变得难以察觉得多。
链接和域名
链接可能包含与熟悉字母相似的字符。
你认出的名称不一定就是你以为的那个确切域名。
代码
方向控制字符和其他不常见的 Unicode 字符,可能让源代码更难被正确审查。
开发者看到的是渲染后的文本。
编译器看到的是实际的字符。
搜索和过滤
过滤器通常是在比较字符串。
插入一个隐形字符,字符串就变了。
这可能影响搜索、屏蔽列表、内容审核系统和其他文本处理工具。
你可以怎样检查?
你不需要成为 Unicode 专家。
有几种简单的检查方法。
检查 1——扫描文本
把可疑的文本粘贴到字符安全扫描器中。
一个有用的扫描器应该告诉你:
- 是否存在隐形字符
- 发现了多少个
- 它们是哪些字符
- 它们属于哪个 Unicode 类别
- 是否检测到形似字符
这正是本网站的扫描器设计来展示的内容。
重要的是,它应该告诉你它实际发现了什么,而不是只给你一个神秘的绿灯或红灯。
检查 2——比较字符数
这是一个快速的合理性检查。
如果一句话看起来有 38 个字符,而字符计数器显示 47 个,那这个差值就值得调查。
字符数不会告诉你多出来的字符是什么。
但它能告诉你,那里有你没看到的东西。
检查 3——在合适的时候重新输入
如果有人发给你一条简短的指令,而你不需要保留原始格式,手动输入重要的部分可以去掉复制文本中的隐形字符。
但请记住:
重新输入并不能让目的地变得可信。
例如,如果有人发给你一个可疑的网址,手动输入它并不能证明网站本身是正当的。
它只是一种避免复制文本中隐藏字符的方法。
检查 4——读出真正的域名
不要只凭认出域名的样子来判断。
如果一个链接很重要,就检查它真正的域名。
对于重要账户,使用你已经保存的书签,或自己输入已知的地址,而不是点击一个意外收到的链接。
同样的原则也适用于用户名和钱包地址:
认出形状不等于核实了字符。
发现隐形字符时不要惊慌
这一点很重要。
隐形并不自动意味着恶意。
有些隐形的 Unicode 字符之所以存在,是因为人们确实需要它们。
例如,零宽连接符会影响各种书写系统中字符的连接方式。
它们也被用在表情符号序列中。
方向控制字符对于正确显示混合了从左往右和从右往左文字的文本是必要的。
所以发现隐形字符并不自动意味着:
“这是一次攻击。”
它意味着:
这里有我看不见的东西。
那意味着什么取决于具体情境。
文本是谁发的?
文本要你做什么?
你准备把它粘贴到哪里?
它会进入 AI 助手、钱包、网站、终端,还是其他会据此行动的系统?
这些问题很重要。
Tags 区块值得格外注意
Tags 字符很不寻常,因为它们的用途是表示信息,同时在正常的文本渲染中保持不可见。
如果一个陌生人发给你的消息中含有一串意料之外的这类字符,那就值得调查。
扫描器可以揭示这些字符的存在,并在适当时解码它们所表示的文本。
但即便如此,检测也无法告诉你发送者的意图。
它告诉你的是文本里有什么。
这是一个有用得多、也站得住脚得多的答案。
干净的扫描能告诉你什么——不能告诉你什么
这是安全工具需要诚实的又一个地方。
扫描器检测的是字符。
它不会读心。
如果扫描器发现了隐形字符,它可以告诉你它们是什么。
它无法告诉你是谁插入的。
它无法证明为什么要插入它们。
如果它什么也没发现,也不意味着消息本身可信。
一句看起来完全正常的话,仍然可能包含谎言、恶意链接或危险的指令。
同样,含有隐形字符的消息也可能完全无害。
自动检测同形字也有局限。扫描器可以检查已知的形似字符和已知的字符集,但任何单项检查都不应被视为已经发现了所有可能的视觉欺骗的保证。
结果告诉你检测到了什么。
文本是什么意思、你是否信任它的来源,仍然需要你自己判断。
值得养成的简单习惯
你不需要害怕复制和粘贴。
只要在你复制的文本即将让某件事发生时放慢速度。
一个 AI 助手。
一个钱包。
一个网站。
一个命令行。
一个账户。
一笔金融交易。
一段代码。
如果文本不是你自己输入的,你不一定知道里面的全部内容。
而你也不必去猜。
在让这些字符发挥作用之前,先检查它们。
只需要几秒钟。
而且在很多情况下,检查是免费的。
本文仅供教育用途。它解释隐形的 Unicode 字符、文字方向控制符和形似字符如何影响数字文本。本页上的示例包含刻意插入的隐形字符,供你自己检查其机制。隐藏的文本是无害的,仅用于演示。
隐形字符在复制粘贴后依然存在,所以扫描器无论如何都能看到它们。如果想省去粘贴,可以使用扫描选中文字的书签。