基于Python unicodedata.category识别与过滤不可见格式控制字符

举报
yd_292837304 发表于 2026/10/31 01:06:59 2026/10/31
【摘要】 深入探讨使用Python标准库unicodedata.category识别并过滤不可见格式控制字符与异常控制符,防范视觉欺骗并保障文本输入的完整性与安全性。

本文由Gemini辅助撰写,示例代码已在Linux Python 3环境执行验证。

1. 业务背景与潜在风险

在处理用户提交的富文本、文件名、登录凭证或配置内容时,字符串表面呈现的内容与其实际包含的 Unicode 码位可能存在显著差异。现代 Unicode 标准包含大量的不可见字符、排版修饰字符以及双向文本控制字符(BiDi Controls)。例如:

  • 零宽空格(Zero Width Space, \u200b):在视觉呈现上宽度为零,常被用于绕过关键字黑名单过滤,或在两个合法词汇间注入隐藏信息。
  • 从右至左覆盖符(Right-to-Left Override, \u202e):强制改变后续文本的阅读和渲染顺序,攻击者常利用该特性伪造文件扩展名(例如将可执行文件伪装为看似安全的文本文件)。
  • 其他 C 类别控制字符(Other Control, Cc):如 ASCII 码段中的退格符、响铃符、取消符等,可能破坏下游终端渲染、破坏固定格式日志或引发解析层语义错乱。

针对此类不可见字符或排版欺骗攻击,单纯依赖正则表达式的字符集枚举往往难以全面覆盖。Python 标准库 unicodedata 模块提供了访问 Unicode 字符数据库(UCD)的底层接口,能够精确获取每个码位的通用类别(General Category),为构建强健的输入清洗过滤器提供了轻量且标准化的解决方案。

2. Unicode 类别与过滤策略

Unicode 标准为每个已分配码位赋予了一个主类别和子类别标识(由两个字母组成):

  • Cf (Other, format):用于控制文本排版和渲染行为的格式控制字符,如零宽连字/非连字、双向流向控制符、软连字符等。绝大部分业务文本输入都不需要保留格式控制符。
  • Cc (Other, control):传统控制字符(码位范围为 U+0000 至 U+001F 以及 U+007F 至 U+009F)。在实际业务场景中,除了标准空白控制符(换行符 \n、回车符 \r、水平制表符 \t)之外,其余控制字符通常应被无条件剔除。

通过遍历字符串并调用 unicodedata.category(char),可以精准检出这两类字符,并在清洗文本的同时记录被拦截字符的详细信息(包括类别及其标准 Unicode 名称)。

3. 完整实现与执行验证

以下实现展示了如何通过 unicodedata.category 和 unicodedata.name 构造通用的文本净化清洗器,对潜在的伪造输入进行隔离,并确保中英文字符与合法排版空白符号正常放行:

import unicodedata

def sanitize_text(text: str) -> tuple[str, list[tuple[str, str, str]]]:
    cleaned = []
    quarantined = []
    for char in text:
        category = unicodedata.category(char)
        # 拦截格式控制字符(Cf)与非标准空白控制字符(Cc)
        if category == "Cf" or (category == "Cc" and char not in "\r\n\t"):
            try:
                char_name = unicodedata.name(char)
            except ValueError:
                char_name = "<UNKNOWN>"
            quarantined.append((char, category, char_name))
        else:
            cleaned.append(char)
    return "".join(cleaned), quarantined

# 模拟包含零宽空格(\u200b)和从右至左覆盖符(\u202e)的视觉欺骗输入
spoofed_input = "admin\u200b\u202eguest.txt"
clean_res, issues = sanitize_text(spoofed_input)

assert clean_res == "adminguest.txt"
assert len(issues) == 2
assert issues[0] == ("\u200b", "Cf", "ZERO WIDTH SPACE")
assert issues[1] == ("\u202e", "Cf", "RIGHT-TO-LEFT OVERRIDE")

# 验证正常中英文字符与合法换行制表符不会被误杀
valid_text = "华为云\tCloud\n2026"
valid_res, valid_issues = sanitize_text(valid_text)
assert valid_res == valid_text
assert len(valid_issues) == 0

print("MATERIAL_OK")

运行上述代码,控制台标准输出打印 MATERIAL_OK,所有断言均验证通过。

4. 关键边界与运维注意事项

在将此类过滤逻辑部署至自动化生产流水线时,需要特别关注以下边界条件与实现细节:

  1. Python 内置 Unicode 数据库版本依赖:unicodedata.category 的判断依据是 Python 解释器编译时捆绑的 Unicode 字符数据库。在跨版本运行(如从 Python 3.9 迁移至 Python 3.12)时,新版本 Unicode 标准中新增的码位可能从未分配状态(Cn)变为分配状态,需关注极端边缘码位在新旧环境中的判定一致性。
  2. 未分配与私有保留码位处理:
    • 对于未分配码位(Cn,Other, not assigned)或私有使用区码位(Co,Other, private use),unicodedata.name() 会抛出 ValueError 异常。代码中必须使用 try...except ValueError 进行兜底防护,避免异常中断整个服务。
    • 若安全策略要求更为严苛,除 Cf 与 Cc 外,通常还应考虑将 Cn 字符一并纳入拒绝名单。
  3. 合法控制字符的业务界定:示例中保留了 \r\n\t,以支持常规的多行文本与结构化排版。然而在单行标识符校验(如用户名、租户 ID、系统文件名)场景下,制表符和换行符同样应当被归入非法控制字符,需依据具体业务接口规范适时调整放行白名单。

5. 官方参考文档

  • Python 官方文档 unicodedata 模块:Python 3 官方文档(对应模块与章节)
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。