Higress AI 数据脱敏(ai-data-masking)插件:敏感词拦截与替换实战指南
【免费下载链接】higress🤖 AI Gateway | AI Native API Gateway项目地址: https://gitcode.com/GitHub_Trending/hi/higress
Higress 的ai-data-masking(AI 数据脱敏)插件是一款运行在认证阶段(优先级 991)的 WASM 插件,用于对 AI 网关请求/响应中的敏感信息进行拦截与脱敏替换,在保证"敏感数据不出域"的前提下让大模型正常参与业务推理。阅读本文后,你将掌握该插件的处理数据范围、全部配置字段与默认值、基于 GROK/正则的替换规则设计,以及流式(SSE)场景下的还原机制与已知限制,并能在自己的 Higress 网关上直接复现文中的完整配置示例。
该插件源码位于 plugins/wasm-rust/extensions/ai-data-masking,核心实现集中在 src/ai_data_masking.rs,配套 README.md 与 README_EN.md 提供了中英文配置参考。
一、功能说明与处理数据范围
插件核心能力只有两件事:对请求/返回中的敏感词进行拦截、替换。
1.1 处理数据范围(三种粒度)
| 范围 | 说明 |
|---|---|
| openai 协议 | 请求/返回中的对话内容(messages[*].content及reasoning_content) |
| jsonpath | 只处理通过 JSONPath 指定的字段 |
| raw | 处理整个请求/返回 body(非 JSON 场景) |
三种模式并非互斥,配置项deny_openai、deny_jsonpath、deny_raw可同时开启,插件按 openai → jsonpath → raw 的顺序逐级处理。
从源码实现看,请求体处理逻辑位于 on_http_request_complete_body:插件首先尝试将 body 反序列化为 JSON;若deny_openai开启且能解析出 OpenAI 协议结构(stream字段 +messages数组),则标记is_openai = true并逐条检查content与reasoning_content;否则继续尝试 jsonpath 命中字段;最后才进入 raw 整包处理。响应方向对应 on_http_response_complete_body,同样按 openai 与 raw 两条路径执行拦截与还原。
1.2 敏感词拦截
- 在数据处理范围内命中敏感词时直接拦截,返回预设错误信息;
- 支持系统内置敏感词库与自定义敏感词(
deny_words)两种来源。
内置词库通过rust-embed在编译期打包进 WASM 产物,数据文件为 res/sensitive_word_dict.txt(约 6.4 万行),由 src/deny_word.rs 的DenyWord::system()加载。词库数据来源为开源项目 houbb/sensitive-word-data。
需要特别注意的是匹配算法:DenyWord::check()使用jieba 分词后逐个词在HashSet中精确比对(见 deny_word.rs),因此deny_words必须配置为单个单词;英文多词短语(如hello world)由于分词粒度问题可能无法匹配。
1.3 敏感词替换
- 将请求数据中出现的敏感词替换为脱敏字符串后再传给后端服务,保证敏感数据不出域;
- 部分脱敏数据在后端服务返回后可自动还原;
- 自定义规则支持标准正则与GROK 规则,替换字符串支持正则变量(如
$domain)。
替换规则由replace_roles数组定义,每条规则包含regex(支持 GROK 语法)、type(replace或hash)、restore(是否还原)、value(替换值)。其核心实现位于 replace_request_msg:
- 纯替换且不还原(
replace+restore=false):直接对全文做replace_all; - 需要还原或 hash 的场景:逐条正则匹配后,用HMAC-SHA256对原文计算十六进制摘要(
hash类型),或按规则生成替换值,并把「脱敏值 → 原始值」的映射写入请求级mask_map(见 ai_data_masking.rs); - 响应阶段遍历
mask_map,把大模型返回内容中的脱敏值替换回原始值后再转发给用户。
由此实现"出域前脱敏、回程时还原",示例见第五节。
二、运行属性
- 插件执行阶段:认证阶段(Authentication Phase)
- 插件执行优先级:
991
这意味着 ai-data-masking 在 Higress 的认证环节即开始生效,早于后续路由与转发逻辑,保证敏感词在被转发到后端前就已完成拦截或脱敏。插件基于 proxy-wasm 的RootContext/HttpContext模型实现,在 ai_data_masking.rs 通过proxy_wasm::main!注册入口,并复用了 Higress WASM Rust SDK 的RuleMatcher规则匹配框架(支持按路由/域名粒度下发不同配置)。
三、配置字段详解
| 名称 | 数据类型 | 默认值 | 描述 |
|---|---|---|---|
| deny_openai | bool | true | 对 OpenAI 协议进行拦截 |
| deny_jsonpath | string | [] | 对指定 jsonpath 字段拦截 |
| deny_raw | bool | false | 对原始 body 拦截 |
| system_deny | bool | false | 开启内置拦截规则 |
| deny_code | int | 200 | 拦截时的 HTTP 状态码 |
| deny_message | string | 提问或回答中包含敏感词,已被屏蔽 | 拦截时 OpenAI 协议返回的 AI 消息 |
| deny_raw_message | string | {"errmsg":"提问或回答中包含敏感词,已被屏蔽"} | 非 openai 拦截时返回的内容 |
| deny_content_type | string | application/json | 非 openai 拦截时返回的 Content-Type 头 |
| deny_words | array of string | [] | 自定义敏感词列表 |
| replace_roles | array | - | 自定义敏感词正则替换规则 |
| replace_roles.regex | string | - | 规则正则(支持内置 GROK 规则) |
| replace_roles.type | [replace, hash] | - | 替换类型 |
| replace_roles.restore | bool | false | 是否在响应中还原 |
| replace_roles.value | string | - | 替换值(支持正则变量) |
上述默认值均与源码中的反序列化逻辑一一对应(见 ai_data_masking.rs 的default_deny_*系列函数),其中deny_code在源码中被解析为u16类型,配置时注意取值范围。
配置解析阶段值得一提的细节:
regex字段由 deserialize_regexp 完成反序列化:配置加载时先尝试把内容当作GROK 模式编译,失败则回退为标准正则编译,两者都不合法时直接报配置错误;deny_jsonpath由 deserialize_jsonpath 解析为jsonpath_rust::JsonPath对象,非法路径会在配置阶段报错;type字段仅接受replace/hash两个取值(deserialize_type)。
四、完整配置示例
system_deny: true deny_openai: true deny_jsonpath: - "$.messages[*].content" deny_raw: true deny_code: 200 deny_message: "提问或回答中包含敏感词,已被屏蔽" deny_raw_message: "{\"errmsg\":\"提问或回答中包含敏感词,已被屏蔽\"}" deny_content_type: "application/json" deny_words: - "自定义敏感词1" - "自定义敏感词2" replace_roles: - regex: "%{MOBILE}" type: "replace" value: "****" # 手机号 13800138000 -> **** - regex: "%{EMAILLOCALPART}@%{HOSTNAME:domain}" type: "replace" restore: true value: "****@$domain" # 电子邮箱 admin@gmail.com -> ****@gmail.com - regex: "%{IP}" type: "replace" restore: true value: "***.***.***.***" # ip 192.168.0.1 -> ***.***.***.*** - regex: "%{IDCARD}" type: "replace" value: "****" # 身份证号 110000000000000000 -> **** - regex: "sk-[0-9a-zA-Z]*" restore: true type: "hash" # hash sk-12345 -> 9cb495455da32f41567dab1d07f1973d # hash后的值提供给大模型,从大模型返回的数据中会将hash值还原为原始值4.1 内置 GROK 规则说明
示例中出现的%{MOBILE}、%{EMAILLOCALPART}、%{HOSTNAME}、%{IP}、%{IDCARD}均来自 GROK 内置规则。源码在 ai_data_masking.rs 中额外注册了两个系统模式:
static SYSTEM_PATTERNS: &[(&str, &str)] = &[ ("MOBILE", r#"\d{8,11}"#), ("IDCARD", r#"\d{17}[0-9xX]|\d{15}"#), ];GROK 模式展开采用迭代替换算法(grok_to_pattern):先解析%{PATTERN:alias}语法,将命名的 GROK 模式逐层展开为最终正则,并支持通过:alias给捕获组命名——这正是邮箱规则中%{HOSTNAME:domain}搭配替换值****@$domain实现"保留域名"替换的底层机制。
4.2 拦截响应形态
命中敏感词后的响应行为在 deny 中实现,与is_openai、stream状态相关:
- 请求方向拦截:直接
send_http_response,返回deny_code状态码与deny_message(OpenAI 场景会包装为 choices 结构); - 非 openai / raw 拦截:返回
deny_raw_message内容与deny_content_type头; - 响应方向拦截(流式):置空响应体并
Continue,避免向用户输出不完整内容。
五、敏感词替换样例(还原链路演示)
5.1 用户请求内容
请将
curl http://172.20.5.14/api/openai/v1/chat/completions -H "Authorization: sk-12345" -H "Auth: test@gmail.com"改成post方式
5.2 处理后请求大模型内容
curl http://***.***.***.***/api/openai/v1/chat/completions -H "Authorization: 48a7e98a91d93896d8dac522c5853948" -H "Auth: ****@gmail.com"改成post方式
可以看到三类处理同时生效:
172.20.5.14(IP)→***.***.***.***,且restore: true会登记还原映射;sk-12345(API Key,hash类型)→48a7e98a91d93896d8dac522c5853948(HMAC-SHA256 摘要);test@gmail.com(邮箱)→****@gmail.com(通过 GROK 别名变量保留域名)。
5.3 大模型返回内容
大模型在完全不知晓原始敏感值的情况下完成改写任务:
curl -X POST \ -H "Authorization: 48a7e98a91d93896d8dac522c5853948" \ -H "Auth: ****@gmail.com" \ -H "Content-Type: application/json" \ -d '{"key":"value"}' \ http://***.***.***.***/api/openai/v1/chat/completions5.4 处理后返回用户内容
插件在响应阶段根据mask_map将脱敏值还原为原始值,用户拿到的结果与原始请求完全一致:
curl -X POST \ -H "Authorization: sk-12345" \ -H "Auth: test@gmail.com" \ -H "Content-Type: application/json" \ -d '{"key":"value"}' \ http://172.20.5.14/api/openai/v1/chat/completions还原逻辑对应响应处理代码中的mask_map.iter()遍历替换(ai_data_masking.rs):先检查响应内容是否含敏感词(命中则拦截),再把脱敏值替换回原始值。
六、流式(SSE)模式下的实现与限制
OpenAI 流式响应以data:前缀的 SSE chunk 形式下发,脱敏/还原必须跨 chunk 拼接处理。插件通过 msg_win_openai.rs 的MsgWindow实现:使用higress_wasm_rust::event_stream::EventStream解析 SSE 事件流,按choices[*].index维护独立的MessageWindowOpenAi滑动窗口,逐 chunk 累积文本后执行敏感词检查与还原替换,并将usage字段通过 number_merge.rs 的NumberMerge做跨 chunk 累加合并,保证重写后的流式响应在结构与统计字段上依然完整合法。非 OpenAI 流则退化为 msg_window.rs 的通用字节窗口。
该窗口模块自带单元测试(msg_win_openai.rs):使用 test/raw_message.txt 中的真实流式报文逐行喂入,验证content与reasoning_content跨 chunk 替换后能无损还原拼接结果。
6.1 相关说明(已知限制)
- 流模式中,如果脱敏后的词被多个 chunk 拆分,可能无法进行还原;
- 流模式中,如果敏感词被多个 chunk 拆分,可能会有敏感词的一部分返回给用户的情况;
- GROK 内置规则列表可参考阿里云 SLS 用户指南的 grok-patterns 文档;
- 内置敏感词库数据来源为 houbb/sensitive-word-data 项目;
- 由于敏感词列表是在文本分词后进行匹配的,请将
deny_words设置为单个单词,英文多单词情况(如hello world)可能无法匹配。
这四点限制均与滑动窗口的实现边界直接相关——窗口重叠区(char_window_size * 2/byte_window_size * 2,见 on_http_response_body)的设计正是为了尽量降低跨 chunk 拆词概率,但仍无法覆盖极端拆分场景,生产使用时建议将脱敏词设计为不易被截断的形态。
七、如何接入与验证
- 该插件为 Higress 的 WASM Rust 插件,构建产物为 cdylib(见 Cargo.toml),当前版本记录在 VERSION(2.0.2)。完整构建与发布流程可参考 plugins/wasm-rust/Makefile 及 plugins/wasm-rust/README.md;
- 在 Higress 中通过 WasmPlugin CRD 将插件挂载到网关或指定域名,并将上文配置示例写入
defaultConfig即可生效;插件声明在认证阶段、优先级 991,会先于普通路由规则执行; - 验证时可先开启
system_deny: true观察内置词库命中行为,再按第四节示例配置替换规则,并用第五节样例中的 curl 请求做端到端对比:比对发往大模型的请求体(应已脱敏)与最终返回用户的内容(应已还原)。
通过合理组合deny_openai/deny_jsonpath/deny_raw三种作用域、replace/hash两种替换类型以及restore还原开关,ai-data-masking 插件可以在 AI 网关链路上实现"敏感数据不出域、业务功能不降级"的合规目标,是 AI 网关数据安全治理中一个可直接落地的方案。
【免费下载链接】higress🤖 AI Gateway | AI Native API Gateway项目地址: https://gitcode.com/GitHub_Trending/hi/higress
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考