关于 Unicode 每个程序员应该知道的 5 件事

本文由码农网 – 小峰原创翻译,转载请看清文末的转载要求,欢迎参与我们的付费投稿计划

上周末,曝出了山寨WhatsApp Android应用程序的新闻,看似由相同的开发者提供作为了官方应用程序。欺诈分子通过在开发者名字中包含unicode非输出空格来避免验证。在Play store的维护人员注意到之前,黑客已经欺骗了一百多万人。

Unicode是一个令人难以置信的有用标准,它能使全世界的计算机、智能手机和智能手表以同样的方式显示相同的信息。不幸的是,它的复杂性使它成为了欺诈分子和恶作剧的金矿。如果谷歌这样的巨头都无法抵御由Unicode引起的基本问题,那么对于小公司来说,这或许就是一场看起来注定失败的战斗。但是,这些问题大都围绕一些漏洞出现。以下是所有开发人员需要了解的关于Unicode以防止欺诈的最重要的5件事情。

1.许多Unicode点不可见

Unicode有若干零宽度的代码点,例如零宽度连接器(U+200D)和零宽度非连接器(U+200C),这些用连字符号连接的工具都是暗示的(hints)。它们对屏幕外观没有可见的影响,但是它们会影响字符串比较,这就是为什么山寨WhatApp能够躲过漏检这么久的原因。这些字符大多数都在常规的标点符号块(从U+2000到U+206F)。一般来说,没有理由允许任何人在标识符中使用此标点符号块的代码点,所以它们最不容易过滤。然而,在这个范围之外还有一些其他的特殊代码是不可见的,比如蒙古文元音分隔符(U+180E)。

通常,使用Unicode对唯一性约束进行简单的字符串比较是很危险的。潜在的解决方法是限制标识符允许的字符集以及可能被欺诈分子滥用的任何其他数据。不幸的是,这并不能完全解决问题。

2.许多代码点看起来非常相似

为了覆盖世界上所有书面语言所使用的所有符号,Unicode不得不具有许多类似的字符,以至于人们无法区分这些字符,但计算机区分差异时则毫无问题。此问题导致了大量滥用Mimic——这是一个有趣的工具,用外观类似的Unicode字符替换软件开发中使用的常见符号,如冒号和分号。它可以造成代码编译工具中的混乱,让开发人员感到困惑。

符号外观类似的问题远远不止是简单的恶作剧而已。虽然有点奇怪被称为是同态攻击,但这些漏洞确实可能会导致严重的安全问题。2017年4月,一名安全研究人员通过混合来自不同字符集的字母,注册了一个与apple.com非常相似的域名,甚至获得了SSL证书。所有的主要浏览器都高高兴兴地显示了SSL挂锁,并将域名列为是安全的。

与混合可见和不可见字符类似,很少有任何理由允许在标识符中使用混合字符集名称,尤其是域名。大多数浏览器已采取措施惩罚混合字符集的域名,将它们显示为十六进制Unicode值,这样用户就不会轻易混淆。如果你要向用户,例如在搜索结果中,显示标识符,那么考虑一些类似的方法以防止混淆。但是,这也不是一个完美的解决方案。某些域名,如sap.com或chase.com完全可以很容易地从非拉丁字符集的单个块中构建起来。

Unicode联盟发布了一个容易混淆的字符列表,可用于自动检查山寨货。另一方面,如果你想寻找创建混淆的快速路径,那么请看Shapecatcher——这是一个奇妙的工具,它列出了看上去类似绘图的Unicode符号。

3.规划化其实不规范

规范化对于如用户名等标识符非常重要,可以帮助用户虽以不同的方式输入值,但能一致地处理它们。规范标识符的常用方法是将所有内容都转换为小写,例如,确保JamesBond与jamesbond相同。

由于有如此多的相似字符和重叠集合,不同的语言或unicode处理库可能会应用不同的规范化策略,这会潜在地开放安全风险,如果规范化在几个地方完成的话。简而言之,不要认为小写变换在应用程序的不同部分中是一样的。来自Spotify的Mikael Goldmann在他们的一个用户发现劫持账户的一个方式之后,于2013年就此问题写了一篇很赞的事件分析。攻击者可以注册其他人的用户名(例如 ᴮᴵᴳᴮᴵᴿᴰ)的unicode变体,这些用户名将被转换为相同规范的帐户名(bigbird)。应用程序的不同层次对字词进行不同的规范化处理——允许用户注册恶意帐户,但是会重置目标帐户的密码。

4.屏幕显示长度和内存大小没有关系

使用基本的拉丁文和大部分欧洲的字符集时,屏幕或纸张上的文本空间大致与符号数量成正比,与文本的内存大小大致也成正比。这就是为什么EM和EN是流行的单位长度。然而通过Unicode,任何类似的假设都会变得危险。有一些可爱的符号,比如Bismallah Ar-Rahman Ar-Raheem(U+FDFD),此单个字符比大多数英文单词都要长,因此很容易在网站上跳出假定的视觉封闭。这意味着基于字符串字符长度的任何类型的换行或文本中断算法都可能轻易被愚弄。大多数终端程序需要固定宽度的字体,所以在其中一个显示的话,你将在完全错误的地方看到结束引号。

对此有一个滥用就是zalgo文字生成器,在文本片段周围添加围绕着的垃圾,而这些垃圾将占据更多的垂直空间。

当然,隐藏代码点的整个问题使得内存大小与屏幕长度无关,所以可以很好适合输入域的东西可能足以炸毁数据库领域。过滤非视觉字符以防止出现问题是不够的,因为还有很多其他不占用空间的例子。

结合拉丁字符(例如U+036B和U+036C)占据上一个字母的空格,这样你可以在单个文本行中写入多行文本(’N\u036BO\u036C’生成NͫOͬ)。用来表示希伯来语圣经仪式吟诵的语调标记可以无限地堆叠在同一个视觉空间中,而这意味着它们可以轻易被滥用,会导致编码大量信息到屏幕上占据单个字符的内容上。Martin Kleppe编码了针对浏览器语调标记的《Conway’s Game of Life》的实现。查看页面的源代码,很不错。

5. Unicode不仅仅是被动数据

一些代码点旨在影响可输出字符的显示方式,这意味着用户可以复制和粘贴的不仅仅是数据——也可以输入处理指令。一个常见的恶作剧是使用从右到左覆盖(U+202E)来切换文本的方向。例如,用谷歌地图搜索Ninjas。查询字符串实际上会翻转搜索词的方向,尽管页面的搜索字段中显示的是“ninjas”,但实际上它搜索的是“sajnin”。

这个漏洞是如此受欢迎,以致于甚至成为了XKCD

混合数据和处理指令——可有效执行的代码——绝不是一个好主意,特别是如果用户可以直接输入的话。这对于包含在页面显示中的任何用户输入来说,都是一个大问题。大多数Web开发人员都知道通过删除HTML标签来清理用户输入,但输入中的Unicode控制字符也需要注意。这是解决任何形式的脏话或内容过滤的简单方法——只需要向后翻转单词,在开始处包含从右到左的覆盖。

从右到左的编辑可能无法嵌入恶意代码,但如果不小心的话,可能会破坏内容或翻页。防止这种情况的常用方法是将用户提供的内容放入输入字段或文本区域,这样处理指令不会影响页面的其余部分。

另一个关于显示特别有问题的处理指令类型是字形变换选取器。为了避免为每个表情符号的每个颜色变体创建单独的代码,Unicode允许使用变换选择器将基本符号与颜色混合。白色旗帜、字形变换选取器和彩虹通常会产生彩虹色的旗帜。但并非所有的变换都是有效的。2017年1月,iOS unicode处理中的一个bug使得恶作剧者可以通过发送特制消息来远程崩溃iPhone。消息包含白色旗帜,字形变换选取器和一个零。这时,iOS CoreText会进入应急模式——尝试选择正确的变体,并使操作系统崩溃。此恶作剧作用于直接信息、分组聊天,以及甚至共享联系人卡片。这个问题对iPad,甚至一些MacBook电脑也有影响。对此,我们几乎束手无策。

类似的bug每隔几年就会发生一次。2013年,阿拉伯字符处理的bug——可能会导致OSX和iOS崩溃——浮出水面。所有这些错误都深深埋藏在OS文本处理模块中,所以典型的客户端应用程序开发人员根本无法阻止。

关于其他有趣的处理指令,请查看GitHub上的Awesome Codepoints列表。对于Unicode造成的更多混乱,请查阅我写的《Humans vs Computers》一书。

译文链接:http://www.codeceo.com/article/5-things-every-programmer-know-about-unicode.html
英文原文:Five things everyone should know about Unicode
翻译作者:码农网 – 小峰
转载必须在正文中标注并保留原文链接、译文链接和译者等信息。]