如何防范 Unicode 双向文本与同形异义字符造成的安全问题?
题目与使用场景
代码审查工具、账号系统或域名展示中,攻击者提交了视觉上相似的字符,或者用双向控制字符改变了阅读顺序。请说明逻辑顺序和显示顺序的差异,解释混淆检测与限制策略,并给出不会破坏正常多语言文本的处理流程。
面试官考察什么
- 是否理解 Unicode 双向算法只影响显示顺序,底层逻辑顺序仍保持不变。
- 能否区分双向覆盖、隔离字符、脚本混合和同形异义检测。
- 是否知道 UTS #39 的 skeleton 是检测中间值,不应直接展示或跨 Unicode 版本复用。
- 能否把输入校验、展示、审计、权限比较和升级迁移分开设计。
作答前的澄清问题
先确认对象是源代码、登录标识、国际化域名、搜索文本还是普通评论;确认是否允许多脚本、目标语言和显示端支持;再确认检测结果是阻断、人工复核、警告还是仅记录。还要确认 Unicode 数据版本、保留原文要求和误报容忍度。
30 秒回答框架
Unicode 文本有逻辑顺序和显示顺序。UAX #9 的双向算法会根据字符方向属性重排显示,但双向控制字符不改变比较、解析或数字分析。安全上要限制危险控制字符,按 UTS #39 做脚本与 confusable 检测;skeleton 只作为同一版本下的中间比较键,不能当展示文本。系统保留原文,生成版本化诊断结果,在高风险标识上阻断或复核,并用固定策略验证权限比较。
分步骤深入解答
1. 先分清逻辑顺序与显示顺序
阿拉伯文、希伯来文与数字混排时,UAX #9 根据强、弱和中性方向类型计算显示顺序。RLO、LRO 等覆盖字符会强制方向,隔离字符则限制内部文本对外部段落的影响。显示重排不改变内存中的码点顺序,也不应被当作解析或比较规则。
2. 识别双向控制和脚本混合风险
源代码、文件名、账号名等结构化标识通常不需要任意方向控制字符。输入层可拒绝或标记 Bidi_Control;展示层显示转义标记或明确方向边界。脚本混合策略应按业务允许的语言集合定义,不能简单地把所有非 ASCII 字符判为恶意。
3. 使用版本化的 confusable 检测
UTS #39 提供 confusables 数据和 skeleton 机制,用于判断视觉混淆。confusability 受字体、脚本和上下文影响,不是绝对等价关系。保存原文、Unicode 版本、脚本分析和检测结果;升级数据后重新计算并审查新增冲突,权限判断仍使用协议规定的精确比较规则。
高质量示范回答
我会先按字段定义风险。对源代码、用户名和域名这类结构化标识,保存原文并限制允许脚本和方向控制字符;对普通多语言正文,则保留合法的双向排版能力。UAX #9 只决定显示顺序,双向控制字符不应改变解析、比较或数字分析,因此审计工具必须同时展示逻辑码点顺序与可见渲染。检测阶段使用 UTS #39 的限制级别、脚本混合规则和 confusables 数据;skeleton 是同一 Unicode 数据版本内的中间键,不能直接展示,也不能跨版本永久复用。高风险字段出现 RLO、异常脚本组合或与既有标识冲突时进入阻断或人工复核,普通文本则记录警告。比较和授权使用字段协议定义的规范化、大小写和编码规则,不能把视觉相似当作相等。升级 Unicode 数据时批量重算并审计冲突,确保变更可追踪和可回滚。
常见错误
- 把显示顺序误认为字符串的实际存储顺序。
- 说删除所有从右到左字符就能解决安全问题,误伤正常阿拉伯文或希伯来文。
- 把 skeleton 当作稳定哈希、展示值或跨版本协议字段。
- 只检查 ASCII,忽略字体、脚本、组合标记和上下文造成的混淆。
- 用视觉相似判断权限、签名或唯一性,跳过字段的精确比较规则。
追问及应对
为什么不能在所有输入上删除双向控制字符?
控制字符确实可能用于欺骗,但合法文档和排版也可能需要方向控制。应按字段风险和目标平台决定阻断、转义、隔离或告警,并保留可审计证据。
skeleton 结果可以直接作为用户名吗?
不可以。它是检测用的中间形式,且会随 Unicode 数据版本变化。用户名仍需保留原文和协议定义的比较键,skeleton 只用于冲突诊断或人工复核。
如何降低多语言场景的误报?
先定义允许的语言和脚本集合,再结合 CLDR 语言数据、字段上下文和人工复核。对评论正文采用告警,对登录标识、域名和代码标识采用更严格的限制。