掩码分叉
给智能体(或人)生产环境的形态而不含其任何 PII——脱敏策略、内置函数库,以及脱敏分支和沙箱如何在脱敏提交前保持封闭。
MASK 是面向敏感数据的 Agent-Safe Change Control 轨道:它交给智能体一个具有生产环境 形态、 却不含其任何 PII 的 fork。它以两种方式工作——脱敏 分支 (面向人:开发、CI、外包),以及脱敏 沙箱 (面向智能体)。两者都在下面涵盖。
数据脱敏 在 分支 创建的那一刻就匿名化敏感列。 你在创建分支的调用上附加一个 脱敏策略,新分支中匹配的列会在 分支可被访问之前就被不可逆地重写——哈希、置空、截断或替换。 父分支永远不会被修改。
当你需要把生产形态的数据交给开发、CI 或 外包人员,却又不想把其中的 PII 一并交出去时,你会用到它:真实的表结构、 真实的行数、伪造的邮箱。
How it works
一个脱敏策略是一组项目范围的、具名的规则。每条规则 按模式匹配列并指定一个内置脱敏函数:
| 字段 | 含义 |
|---|---|
schema_pattern | 模式(schema)名称匹配模式(% 或 * = 任意,_ = 一个字符)。默认为 *。 |
table_pattern | 表名匹配模式。 |
column_pattern | 列名匹配模式。 |
masking_fn | 下列内置函数之一。 |
fn_args | 函数参数(只有 mask_constant 接受一个:value)。 |
在创建分支时,当提供了 masking_policy_id:
- 分支照常从其父分支派生(写时复制——瞬时完成)。
- 分支进入
masking状态。不会预配任何端点, 代理也拒绝对它的连接:不存在任何窗口 能读取到脱敏前的数据。 - 一个脱敏工作器以最小权限角色连接到该分支的计算, 发现其模式,将你的规则 与之匹配,并在单个事务中执行每一次重写。
- 分支落入
ready且其端点启动——此时只 提供脱敏后的数据。一旦出现任何失败,分支落入failed并保持封闭;删除它并重试。
规则输入始终被当作数据处理,绝不当作 SQL:标识符会被加 引号,参数值在执行时作为参数绑定,因此一个恶意的 列名或常量无法突破重写。
Built-in functions
| 函数 | 效果 |
|---|---|
mask_email | md5(value)@masked.invalid |
mask_name | Name_ + 一个 8 字符的哈希前缀 |
mask_null | NULL(列必须可为空) |
mask_constant | 你提供的一个固定值(fn_args.value) |
mask_ssn_partial | ***-**-1234 —— 保留最后 4 位 |
mask_credit_card | ****-****-****-1234 —— 保留最后 4 位 |
mask_ip | 0.0.0.0 |
mask_date_year | 保留年份,截断到 1 月 1 日 |
mask_hash | md5(value) |
mask_shuffle | 基于哈希的打乱(完整的字符乱序在计划中) |
mask_phone | 一个合成的 +1-555-XXXX 号码 |
mask_uuid | 一个全新的随机 UUID |
该目录也由 API 提供:
curl -s https://api.kisenon.com/v1/masking-functions \
-H "Authorization: Bearer $KISENON_API_KEY"Managing policies
在控制台中,打开 Project settings → Data masking 来创建一个 策略:为其命名,添加规则(模式列 + 一个函数下拉框),然后 保存。同样的入口也存在于 API 上:
curl -s -X POST \
https://api.kisenon.com/v1/projects/$PROJECT_ID/masking-policies \
-H "Authorization: Bearer $KISENON_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "dev-safe",
"rules": [
{"table_pattern": "users", "column_pattern": "email", "masking_fn": "mask_email"},
{"table_pattern": "users", "column_pattern": "phone", "masking_fn": "mask_null"},
{"table_pattern": "%", "column_pattern": "%ssn%", "masking_fn": "mask_ssn_partial"}
]
}'然后通过把策略加入一个普通的创建分支调用来创建一个 脱敏分支:
curl -s -X POST \
https://api.kisenon.com/v1/projects/$PROJECT_ID/branches \
-H "Authorization: Bearer $KISENON_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "masked-dev", "masking_policy_id": "'$POLICY_ID'"}'重写运行期间该分支报告 state: "masking";在控制台中(实时,
通过项目 事件流)或通过轮询 GET /v1/branches/{id}
观察它翻转为 ready。
Masked sandboxes (for agents)
同样的策略脱敏一个智能体的 沙箱 fork,因此 一个智能体在生产环境的形态上工作,却永远看不到它的 PII。在创建时 附加一个策略:
keon sandbox create --project <id> --masking-policy <policy-id>脱敏在沙箱激活 之前 运行。沙箱报告
masked: true 及其 masking_policy_id,并且在它仍处于 creating 时,
一个 masking 进度对象(state、phase、rules_total、
rules_completed、rows_affected_so_far、error)显示重写
推进中。
设定一个每项目的下限,使每个沙箱默认都被脱敏:
keon projects update <id> --sandbox-masking-policy <policy-id> # or: none一个每请求的 --masking-policy 覆盖项目默认值,但它
绝不能选择退出 一个强制项——这是对一个智能体
说服自己脱离脱敏的一道防御。设定该强制项仅限 owner/admin;
智能体能力密钥会被拒绝(403)。
构造上失败即关闭。 一个脱敏 fork 的端点 诞生 于一个
脱敏状态——代理拒绝连接(SQLSTATE 57P05),直到
重写提交。如果脱敏步骤失败或未接线,创建会失败
即关闭:绝不会为一个未脱敏的 fork 签发任何 URL。脱敏工作器
以最小权限角色 kisenon_masker 连接(绝非超级用户),
不持有任何集群 RBAC,且其每次运行的密码绝不被持久化或记录。
两个值得知道的注意事项:
- 针对脱敏值的 DML 在重放时不会在父分支上匹配。 一条
像
UPDATE … WHERE email = 'a1b2@masked.invalid'的语句针对一个 在main上并不存在的脱敏值。模式通道的工作是 与值无关的,会干净地提升;依赖值的 DML 不是一个脱敏 fork 该做的活。 - 外键仍被强制。 脱敏禁用 用户 触发器
(
ALTER TABLE … DISABLE TRIGGER USER),而非引用完整性触发器。 因此脱敏一个处于外键关系中的列可能命中一个 RI 错误并 让 fork 失败即关闭,而非悄悄破坏该引用。 - v1 仅脱敏
main数据库。
Good to know
- 脱敏是一次性的,发生在创建时。 编辑一个策略永远不会触及那些 已经用它创建的分支——它们保持自己诞生时的数据形态。 重新创建分支以应用新规则。
- 使用中的策略无法删除。 删除一个有活动
分支用它创建过的策略会返回
409 policy_in_use;先删除那些 分支。 - 未匹配的规则会被跳过,而非致命。 如果你的模式发生了漂移而 某个模式什么也匹配不到,分支仍会完成——如果你预期被脱敏的 某列仍保留着真实的数据形态,请检查规则模式。
- 类型不匹配会使分支失败。 一条匹配到其函数无法重写的列的
规则(比如对一个
integer用mask_email)会中止整个脱敏—— 分支落入failed,绝不会以脱敏到一半的状态暴露。