matinfo/pii-airlock

GitHub: matinfo/pii-airlock

一款基于 Microsoft Presidio 构建的本地可逆 PII 脱敏工具,通过 CLI 管道、通用网关代理和 Claude Code hooks 三种方式防止真实个人数据在使用 AI 工具时泄露。

Stars: 0 | Forks: 0

# pii-airlock [![CI](https://static.pigsec.cn/wp-content/uploads/repos/2026/06/948797beb9051133.svg)](https://github.com/matinfo/pii-airlock/actions/workflows/ci.yml) [![PyPI](https://img.shields.io/pypi/v/pii-airlock.svg)](https://pypi.org/project/pii-airlock/) ![Python](https://img.shields.io/badge/python-3.10%E2%80%933.14-blue.svg) ![平台](https://img.shields.io/badge/platforms-macOS%20%7C%20Linux%20%7C%20Windows-lightgrey.svg) [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff) [![许可证: MIT](https://img.shields.io/badge/license-MIT-yellow.svg)](LICENSE) **[安装](#install) · [网关](#universal-gateway-any-provider) · [CLI](#cli-usage) · [Claude Code](#claude-code-hooks) · [所有代理 →](AGENTS.md) · [安全](#-security-the-mapping-file-holds-real-pii)** 基于 [Microsoft Presidio](https://microsoft.github.io/presidio/) 构建的本地、**可逆** PII 匿名化工具。 将真实的个人数据替换为稳定的占位符 token,将脱敏后的文本发送给模型,然后再从本地映射文件中将原始数据替换回来。 **检测和替换完全在你的本地机器上运行。** CLI 管道和 Claude Code hooks 不会向任何地方发送数据。可选的网关*确实*会将流量转发到你指定的提供商 —— 但这仅发生在 PII 被替换为 token **之后**。真实的个人数据永远不会到达提供商处。 支持在 **macOS、Linux 和 Windows** 上运行,要求 Python ≥ 3.10。默认支持检测英语和法语,可通过配置支持 [任何 spaCy 语言](#adding-a-language)。 ## 三种工作流 | 工作流 | 功能 | 适用范围 | |---|---|---| | **网关** (proxy) | 在传输过程中进行透明的脱敏/还原 | OpenAI, Codex, Anthropic, Gemini, Cursor, Continue, … | | **CLI 管道** | `pii-airlock scrub` → LLM → `pii-airlock restore` — 可逆、可脚本化 | 任何场景 | | **Claude Code hooks** | 在 PII 到达模型之前(包括 prompt 和工具数据中)进行检测 | Claude Code | 这三种方式共享同一个检测引擎 —— 提供商特定的知识仅存在于微小的 payload adapter 中。 ## 安装 要求 Python ≥ 3.10 以及 [pipx](https://pipx.pypa.io/)(推荐)或 pip。 在 macOS、Linux 和 Windows 上的工作方式完全相同。 ``` pipx install "pii-airlock[proxy]" # gateway-ready install (recommended) pii-airlock download-models # one-time: fetch NLP models (en + fr) ``` 从源码安装最新版本(在版本发布到 PyPI 之前): ``` pipx install git+https://github.com/matinfo/pii-airlock ``` 可选的格式支持(纯文本、CSV 和 JSON 默认即可使用): ``` pipx inject pii-airlock 'pii-airlock[proxy]' # universal gateway (any provider) pipx inject pii-airlock 'pii-airlock[docx]' # Word .docx pipx inject pii-airlock 'pii-airlock[pdf]' # PDF (text extraction) pipx inject pii-airlock 'pii-airlock[all]' # everything ``` 如果 `download-models` 报告你的解释器没有 `pip`(在 `pipx` 中很常见),请运行打印出的 `pipx inject pii-airlock ""` 命令。该命令现在会为你打印出精确的 model wheel URL。 ## 通用网关(任何提供商) 该网关是一个**本地反向代理**。通过客户端的 base-URL 设置将任何 LLM 客户端指向它;代理会清除出站请求中的 PII,并在响应中还原真实值 —— 包括流式响应。客户端完全察觉不到差异。 ``` your app ──http──▶ pii-airlock gateway ──https──▶ provider API ◀─────────── (restore) ◀─────────── (scrub) ``` ``` # 如果您通过 "pii-airlock[proxy]" 安装,则已包含 # pipx inject pii-airlock 'pii-airlock[proxy]' pii-airlock proxy # listens on http://127.0.0.1:8745 ``` 然后在你使用的任何客户端中设置 base URL: ``` # OpenAI SDK / Codex CLI / 大多数兼容 OpenAI 的工具 export OPENAI_BASE_URL=http://127.0.0.1:8745/openai # Anthropic SDK export ANTHROPIC_BASE_URL=http://127.0.0.1:8745/anthropic # Google Gemini — 使用 base path # https://generativelanguage.googleapis.com -> http://127.0.0.1:8745/gemini ``` **为什么使用代理?** 这是唯一的一个拦截点,它同时具备透明性(只需配置一次)、双向性(自动清除 prompt *并* 还原响应)、通用性(每个提供商都使用 HTTP 通信)和可强制执行性(只知道代理 URL 的客户端无法绕过它泄露数据)。 - **无需 TLS 拦截。** 你的客户端通过纯 HTTP 与 `localhost` 通信;由代理向上游发起真实的 HTTPS 调用。无需安装任何证书。默认绑定在 `127.0.0.1`。 - **身份验证直接透传**,pii-airlock 绝不会记录请求头或请求体。(uvicorn 在 `warning` 日志级别运行,因此也不会记录请求行。) - **每个请求使用全新的内存映射** —— 网关不会向磁盘写入任何内容。 - **并发安全:** 检测过程通过锁进行了串行化处理,因此可以安全地在并发请求之间共享引擎。 **提供商覆盖范围** —— 三种传输格式,每种在 `pii_scrub/payload.py` 中都有对应的 adapter: | 路由 | 传输格式 | 覆盖范围 | 流式传输 | |---|---|---|---| | `/openai` | OpenAI Chat Completions | OpenAI, Codex, Cursor, Continue, Ollama, LiteLLM, vLLM, … | SSE ✅ | | `/anthropic` | Anthropic Messages | Claude SDK, 兼容 Claude 的工具 | SSE ✅ | | `/gemini` | Gemini generateContent | Google Gemini | SSE ✅ · array-stream 已缓冲 | Adapter 已通过单元和集成测试(模拟上游)针对每个提供商的文档请求/响应结构进行了验证。添加一个提供商 = 只需编写一个小型的 adapter。 ## CLI 用法 ### 可逆的脱敏 → 还原管道 ``` # 1. 清理 — 将 PII 替换为 tokens,并保存映射文件 echo "Contacte Jean Dupont à jean@acme.fr" \ | pii-airlock scrub --map /tmp/m.pii-map.json # → Contacte à # 2. 将清理后的文本发送到您的 LLM … # 3. 恢复 — 在模型的响应中替换回 tokens echo "J'ai répondu à via ." \ | pii-airlock restore --map /tmp/m.pii-map.json # → J'ai répondu à Jean Dupont via jean@acme.fr. ``` 相同的值总是会获得相同的 token(``),因此模型依然能够识别共指关系。 ### 仅检测不修改文本 ``` pii-airlock detect notes.txt # PERSON 0.85 [9:21] 'Jean Dupont' # EMAIL_ADDRESS 0.99 [24:38] 'jean@acme.fr' ``` ### 文件格式 ``` pii-airlock scrub report.csv -o report.scrubbed.csv pii-airlock scrub data.json -o data.scrubbed.json pii-airlock scrub contract.docx -o contract.scrubbed.docx # requires [docx] pii-airlock scrub scan.pdf # requires [pdf] → text on stdout ``` ### 其他选项 ``` pii-airlock scrub prompt.txt --no-map # irreversible one-way scrub pii-airlock scrub --lang fr,en # explicit language list pii-airlock scrub --threshold 0.7 # raise confidence cutoff pii-airlock scrub input.txt -o out.txt --map secrets.pii-map.json ``` ## Claude Code hooks 注册防护拦截,在 PII 到达模型之前将其拦截: ``` pii-airlock install-hook # both events, project .claude/settings.json pii-airlock install-hook --scope user # ~/.claude/settings.json (all projects) pii-airlock install-hook --event tool # PreToolUse only pii-airlock install-hook --event prompt # UserPromptSubmit only ``` | 泄露途径 | 覆盖方式 | |---|---| | 你在 prompt 中输入的 PII | `UserPromptSubmit` | | Claude 读取的文件中的 PII | `PreToolUse` | | Claude 运行的 shell 命令中的 PII | `PreToolUse` | 这些 hook **负责检测并警告/询问** —— 它们不会静默重写 payload(hook API 不支持原地重写)。如果需要静默、可逆的重写,请使用上方的 CLI 管道。 在配置中设置 `hook_decision: deny` 可直接阻止而不是询问。 ## 配置 覆盖链(从低到高优先级): ``` bundled defaults → ~/.config/pii-airlock/config.yaml → ./.pii-airlock.yaml → CLI flags ``` 默认配置(`config.default.yaml`): ``` languages: [en, fr] models: en: en_core_web_lg fr: fr_core_news_lg score_threshold: 0.5 entities: [] # empty = all entities Presidio recognizes hook_decision: ask # ask (surface + confirm) | deny (block) ``` ### 添加语言 ``` python -m spacy download de_core_news_lg ``` ``` # .pii-airlock.yaml languages: [en, fr, de] models: de: de_core_news_lg ``` ## 保证与限制 **pii-airlock 的保证** - **精确的可逆性。** 引擎进行过 token 化的任何值都能通过映射逐字节地还原。对于被 token 化的片段,`restore(scrub(text))` 可以完全还原。 - **确定性。** 在同一个映射中,相同的值会获得相同的 token,因此模型可以保留共指关系。 - **token 是不透明且安全的。** 还原的值会通过正确的 JSON 编码重新插入;包含引号、换行符或 `<…>` 的值不会破坏 payload。 - **纯本地检测。** 检测过程绝不会发起网络调用。只有网关会转发(已经脱敏的)流量。 **它*不能*保证的内容 —— 请务必阅读** - **检测是尽力而为的,并不彻底。** Presidio + spaCy 基于统计学;它们会漏掉或错误标记实体(在使用 `_sm` 模型或处理包含奇怪空格的电话号码时尤为明显)。pii-airlock 只是降低了风险 —— 它**不能**保证移除所有的 PII。请谨慎审查敏感材料;如有需要,请提高 `score_threshold` 或添加自定义 recognizer。 - **网关作用范围。** 仅对生成 endpoint(chat/messages/generateContent)进行脱敏。Embeddings 及其他 endpoint 将原样透传。Token 仅在消息 **content** 中还原,不会在模型可能输出的 tool-call/function 参数内部还原。 - **Gemini 数组流式传输**(不使用 `alt=sse`)会被缓冲,然后作为一个完整的响应还原,而不是实时流式传输。 - **`.docx`** 的脱敏过程会将修改过的段落重写为单一的 run,因此这些段落内的行内格式不会被保留。**PDF** 仅支持提取 → 输出脱敏后的文本(不会重新渲染 PDF)。 ## 平台支持 已在 CI 中的 **Linux、macOS 和 Windows** 上经过测试(Linux 上为 Python 3.10–3.14;macOS/Windows 上为 3.12–3.13)。一个平台细节: - 映射文件在 **POSIX**(macOS/Linux)上以 **`0600`** 权限创建。**在 Windows 上** `chmod` 无效;文件会继承你账户的默认 ACL —— 在主目录中通常已经是用户私有的。但无论如何,请将映射文件视为机密(见下文)。 ## ⚠ 安全:映射文件包含真实的 PII `*.pii-map.json` 以纯文本形式包含**真实的个人数据**。 - 创建时仅限所有者可读(POSIX 上为 `0600`;Windows 上为默认的用户 ACL —— 见上文)。 - 内置的 `.gitignore` 已排除 `*.pii-map.json` 和 `*.pii-map.*`。 - **永远不要提交映射文件。** - 当你不再需要还原数据时,请删除它们。 - 当不需要可逆性时,请使用 `--no-map`。 - 网关仅将映射保留在内存中,并在每次响应后将其丢弃。 ## 开发 ``` git clone https://github.com/matinfo/pii-airlock cd pii-airlock pip install -e ".[dev]" ruff check . pytest -q ``` 单元测试 stub 了 Presidio/spaCy,并且完全在离线状态下运行。要进行实时端到端检查,请先运行 `pii-airlock download-models`,然后运行: ``` echo "Call John Smith at john@example.com" | pii-airlock scrub --map /tmp/test.pii-map.json ``` ## 许可证 [MIT](LICENSE) © pii-airlock 贡献者
标签:AI隐私保护, API网关, Blue Team, Microsoft Presidio, PII脱敏, Python, 无后门, 逆向工具