
在实际的大模型应用工程中很多人只关注提示词注入、API Key 泄露和模型本身的安全对齐却忽略了一个更隐蔽的数据泄漏入口LLM 的 reasoning-trace也就是模型在给出最终回答之前产生的中间推理过程。这类内容一旦被日志系统、调试工具或业务代码意外采集并提交进代码仓库就可能同时把系统提示词、内部工具调用参数、数据库连接信息甚至明文凭据一起带出去。Aileaks 这类仓库扫描工具本质上要做的事情就是在这些敏感内容被合并进代码库之前把搜索和识别工作自动化。这篇文章会从 reasoning-trace 为什么危险讲起再分析 Aileaks 的扫描思路、典型规则、运行方式和结果解读最后补充一套可以直接落地的仓库泄露排查清单。文章默认读者已经了解 Git 基本操作并且写过至少一个调用过 LLM 接口的小项目。1. 先理解 LLM 推理轨迹为什么比普通日志更危险1.1 推理轨迹包含哪些内容LLM 推理轨迹是模型在产出最终答案前生成的一段内部思考过程。在 OpenAI 的 o 系列模型、DeepSeek 的推理模型、通义千问的部分版本以及各类开源 reasoning 模型中模型会先输出一段“思考草稿”再输出正式答案。这段草稿通常以特殊字段或独立结构保存。从工程视角看这段推理轨迹往往包含模型对系统提示词的理解和复述模型计划调用的工具名称、参数和调用顺序模型从上下文里提取的关键字段模型尝试构造请求时拼接的 URL、请求头和认证信息模型对可疑输入的判断过程和中间指令正因为它是“思考过程”它比最终答案更接近原始数据。如果推理轨迹被完整记录到日志文件随后日志文件被提交到仓库就相当于把模型看到的内部信息和它准备执行的内部动作一起暴露了出去。1.2 泄露后果不是“多一段日志”这么简单推理轨迹泄露的后果通常分四个层次第一层是敏感信息泄露。用户问题中可能包含身份证号、手机号、内部项目代号、数据库表名、密钥片段。推理轨迹会把这些信息重新组织并输出最终落在日志里。第二层是系统提示词泄露。系统提示词往往是业务方投入了大量精力设计的规则包括安全限制、拒绝策略、工具使用约束。一旦进入公开仓库攻击者就可以针对提示词内容构造绕过样本。第三层是工具调用细节泄露。LLM Agent 场景中推理轨迹会记录模型准备调用哪个函数、传什么参数、期望拿到什么结果。攻击者据此可以判断系统内部工具链结构为后续攻击做铺垫。第四层是凭据泄露。部分 Agent 框架会在推理阶段拼接 API 地址、Access Key、Token 甚至数据库密码。如果采样点恰好发生在请求构造阶段凭据就可能被写入推理轨迹。这也是 Aileaks 这类工具把扫描目标定为“reasoning-trace secrets”的原因它扫描的不是普通代码漏洞而是大模型应用开发过程中特有的一类数据泄漏痕迹。1.3 与普通密钥扫描工具和 GitHub 泄露监控的差异传统密钥扫描工具通常扫描仓库中形如sk-、AKIA、ghp_的固定前缀本质是正则匹配。LLM 推理轨迹泄露则不同它可能不包含任何标准密钥格式只是一段 JSON 或 Markdown 格式的思考过程但里面夹带着数据库连接字符串、内侧 API 域名、内部用户 ID、模型规划的服务端请求参数。Aileaks 的侧重点是把“推理轨迹特征”和“敏感字段特征”结合起来扫描先识别哪些文件可能包含推理轨迹再从这些文件里提取机密相关内容最后给出定位、风险等级和修复建议所以它解决的问题不是取代传统 Secret Scanner而是补上大模型应用开发场景里的盲区。2. Aileaks 的设计思路与扫描流程2.1 扫描对象仓库中的三类痕迹Aileaks 在扫描仓库时会重点关注三类痕迹。第一类是推理字段名。不同框架对推理轨迹的字段命名不一样常见的有reasoning、reasoning_trace、thought、thinking、internal_thoughts、chain_of_thought等。文件中一旦出现这类字段说明可能存在未脱敏的模型中间输出。第二类是模型响应结构。有些仓库会保存 LLM 的完整响应体包含choices、message、content、reasoning_content等字段。如果响应体被完整提交推理轨迹就在里面。第三类是日志与调试文件。包括包含trace.log、debug_output、prompt_log、agent_trace等名称的文件。这类文件往往记录了完整的输入输出最容易携带敏感信息。2.2 常见扫描策略特征匹配加风险加权Aileaks 可以按下面这个分层逻辑实现扫描算法先遍历仓库文件过滤二进制文件、依赖目录和.git目录。对文本文件做内容识别判断是否包含推理轨迹字段。对命中的文件再执行敏感规则检测包括密钥特征、URL 特征、IP 特征、JSON 字段特征。综合推理轨迹特征和敏感特征输出风险评分。高置信度命中直接标为高危低置信度命中标为需人工复核。这个过程看起来不复杂但落地时容易踩坑。例如很多推理模型返回的内容里包含代码块正则匹配时容易把正常的代码字符串误判为敏感信息。所以扫描工具在实现时一般还会加上误报抑制逻辑例如排除测试用例中的fake_key、example_token等模拟值。2.3 需要扫描哪些文件类型大模型应用项目里推理轨迹可能出现在以下文件中文件类型示例路径风险说明日志导出文件logs/agent_trace.jsonl包含完整请求响应最危险调试输出debug/response_dump.json开发调试时导出容易忘记删除测试用例tests/test_prompt_output.json可能包含模拟响应也容易混入真实数据Notebookanalysis/prompt_debug.ipynb单元格里可能有未脱敏输出数据库备份backup/trace_records.sql推理轨迹若入库备份也会泄露Markdown 文档docs/example_output.md做示例时复制真实输出易夹带信息错误报告errors/llm_error.txt报错时会打印请求体如果项目里存在上述路径扫描优先级要调高。2.4 最小扫描示例Python 实现思路下面的代码只用于说明扫描思路实际使用 Aileaks 前要确认它的命令行参数和规则格式。import os import re REASONING_FIELD_PATTERNS [ r[\]reasoning_trace[\]\s*:, r[\]thought[\]\s*:, r[\]internal_thoughts[\]\s*:, r[\]chain_of_thought[\]\s*:, r[\]reasoning_content[\]\s*:, ] SENSITIVE_PATTERNS [ (r(?i)sk-[a-zA-Z0-9]{20,}, openai_api_key), (r(?i)AKIA[0-9A-Z]{16}, aws_access_key), (r(?i)(password|passwd|pwd)\s*[:]\s*[\][^\][\], plain_password), (rjdbc:(mysql|postgresql)://[^\s\], jdbc_url), (r(?i)(token|access_key|api_key)\s*[:]\s*[\][^\][\], generic_token), ] def scan_file(filepath): with open(filepath, r, encodingutf-8, errorsignore) as f: content f.read() has_reasoning any( re.search(pattern, content) for pattern in REASONING_FIELD_PATTERNS ) if not has_reasoning: return [] findings [] for pattern, name in SENSITIVE_PATTERNS: for match in re.finditer(pattern, content): start_line content[:match.start()].count(\n) 1 findings.append({ file: filepath, line: start_line, rule: name, match: match.group(0) }) return findings def scan_repo(root): findings [] for dirpath, dirnames, filenames in os.walk(root): dirnames[:] [d for d in dirnames if d not in {.git, node_modules, .venv}] for filename in filenames: filepath os.path.join(dirpath, filename) try: findings.extend(scan_file(filepath)) except Exception: continue return findings if __name__ __main__: result scan_repo(.) for item in result: print(f{item[file]}:{item[line]} [{item[rule]}] {item[match]})这段代码的关键点是先判断文件是否存在推理轨迹字段再执行敏感规则检测。如果不加第一层判断在整仓执行密钥正则结果会充满大量误报。加了推理轨迹字段作为前置条件后扫描目标就锁定在大模型应用特有的泄露场景上误报比例会明显下降。3. 环境准备和运行 Aileaks 扫描3.1 环境要求Aileaks 的具体安装方式依赖原始项目仓库说明。如果原始 README 没有给出版本要求这里提供一套保守的环境准备思路。建议环境如下环境项建议配置原因操作系统Linux 或 macOS大多数扫描工具优先支持 Unix 环境Python3.10 及以上项目可能用到新语法和类型标注Git2.30 及以上需要处理较新的 Git 仓库结构权限普通用户权限不要用 root 运行扫描器网络仅安装依赖时需要离线环境需要提前准备依赖包在正式扫描前先用下面的命令确认环境python --version git --version pip --version如果项目使用 Go 或 Node.js 实现还需要提前安装对应运行时。安装第三方工具前建议先在隔离环境里安装避免依赖冲突。3.2 安装方式如果 Aileaks 发布了 pip 包安装方式可能如下pip install aileaks如果没有发布包也可以从源码安装git clone 仓库地址 cd aileaks pip install -r requirements.txt pip install .源码安装时要注意 Python 版本。依赖安装结束后可以用帮助命令验证是否可用aileaks --help如果命令不存在可能是 Python 脚本目录没有加入 PATH。可以尝试python -m aileaks --help3.3 第一次扫描建议从实验仓库开始不要直接对生产仓库跑扫描。建议先准备一个实验仓库里面放一个模拟推理轨迹文件用来验证 Aileaks 的响应是否符合预期。先创建实验目录mkdir aileaks-demo cd aileaks-demo git init然后创建一个包含模拟推理轨迹的 JSON 文件{ request_id: demo-001, user_input: 请查询订单 20241029 的支付状态, reasoning_trace: 我需要调用 query_order 工具传入 order_id20241029数据库连接使用 jdbc:mysql://internal-db:3306/orders?useroms_ropasswordDemoPass123, response: 订单 20241029 状态为已支付 }这个文件模拟了大模型应用中最常见的泄露场景推理轨迹中拼接了内部数据库连接地址和密码。保存为demo_output.json然后运行扫描aileaks scan .预期结果应该包含demo_output.json文件并标注发现数据库连接串或明文密码。如果扫描器没有命中需要检查它是否会识别reasoning_trace这个字段名以及敏感规则是否包含jdbc:前缀。4. 扫描结果如何分析和降噪4.1 结果字段说明Aileaks 扫描结果通常会包含以下字段字段含义使用建议file_path命中文件路径用于定位问题文件line_number命中行号结合 Git Blame 定位责任人rule_id触发的规则编号判断是哪类敏感信息matched_content命中的原始内容用于人工复核confidence置信度高危还是疑似reasoning_field命中哪个推理字段判断是否确实存在推理轨迹拿到结果后不要只看有没有红色告警要按风险等级逐条核对。confidence为高且匹配到数据库连接串、Token、Access Key 的内容应该当作真实泄露处理。confidence为低且匹配到的只是示例文本可以先标记为待清理不需要立刻吊销密钥。4.2 处理误报的常见方式扫描工具最怕的不是漏报而是误报太多导致团队不再信任结果。Aileaks 这类工具通常会提供忽略规则配置。例如下面的配置文件表示只扫描 JSON、日志和 Python 文件并忽略 test fixture 目录include_extensions: - .json - .jsonl - .log - .py - .md exclude_paths: - tests/fixtures - examples - node_modules ignore_values: - example - fake - test - your-token在团队落地时建议把误报规则维护成独立的配置文件定期 review。不要把明显是敏感信息的命中加入忽略规则否则等于屏蔽了报警。4.3 验证命中是否真实的一种可靠流程当扫描器报告某一行命中了数据库密码时建议按下面的流程处理先打开对应文件查看上下文。确认password字段后面跟着的值是否为空字符串、占位符或示例值。再看 Git 历史。使用git log -L line:file或git blame查看这行是谁在什么时间加进去的。如果确认是真实凭据立即在代码仓库和所有历史提交里清除并前往对应云平台吊销密钥、重置密码。如果确认是模拟值就在扫描配置里加入忽略规则避免下次误报。最后更新团队规范要求在生成示例输出前执行脱敏。这里要注意即使当前版本删掉了密钥只要历史提交里存在就仍然处于泄露状态。修复时必须处理 Git 历史而不是只改当前版本。5. 把 Aileaks 接入日常开发流程5.1 本地提交前检查一种实用的接入方式是在项目根目录添加 Git 钩子在pre-commit阶段执行扫描。下面是一个简单的 Bash 钩子示例#!/bin/bash echo Running Aileaks pre-commit scan... aileaks scan --staged if [ $? -ne 0 ]; then echo Aileaks found sensitive content. Please check before commit. exit 1 fi exit 0将脚本保存为.git/hooks/pre-commit并赋予可执行权限chmod x .git/hooks/pre-commit这个方案的局限是只对本地生效不同开发者需要各自配置。如果需要团队统一执行更适合使用 pre-commit 框架或 CI 流水线。5.2 CI 阶段扫描在 CI 中扫描时建议只扫描新增代码和新增文件避免历史命中一直阻塞构建。以 GitHub Actions 为例可以使用pull_request触发扫描并限制在paths层面name: aileaks-scan on: pull_request: types: [opened, synchronize] jobs: scan: runs-on: ubuntu-latest steps: - uses: actions/checkoutv4 with: fetch-depth: 0 - name: Set up Python uses: actions/setup-pythonv5 with: python-version: 3.11 - name: Install Aileaks run: pip install aileaks - name: Scan changed files run: | changed_files$(git diff --name-only --diff-filterACMRT ${{ github.event.pull_request.base.sha }} ${{ github.sha }}) echo Changed files: $changed_files for file in $changed_files; do aileaks scan $file done这个流程能让泄露在 MR 阶段就被拦截而不是等合入主分支后再补救。5.3 定期全量扫描MR 阶段检查只能覆盖新增内容历史仓库中可能已经存在问题。建议按周期执行全量扫描例如每周一次并把扫描结果作为安全报表归档。全量扫描时重点关注扫描所有分支而不只是默认分支扫描 Git 历史中不同版本对高置信度命中进行二次人工确认对修复后的文件做复扫确认不再命中5.4 与通知渠道打通当扫描命中高危规则时可以触发通知。最轻量的方式是解析扫描结果抽取file_path和rule_id然后发送到团队群机器人。这样可以保证发现泄露的第一时间有人处理。import json import requests def send_alert(findings): for finding in findings: if finding[confidence] high: requests.post(https://example.internal/alert, json{ project: my-llm-app, file: finding[file_path], rule: finding[rule_id] })注意不要把扫描出来的敏感内容原样发送到聊天工具里否则等于二次泄露。通知里只保留文件路径、规则类型和风险等级就够了。6. 推理轨迹中的典型泄露模式6.1 明文数据库凭据这是最危险的泄露模式。在 Agent 工具调用场景中模型可能会把查询参数拼接进 SQL 或 JDBC URL。如果开发者在调试阶段把请求体打印到日志再导出日志到仓库数据库地址和密码就进去了。典型特征字符串以jdbc:开头包含password、pwd、passwd包含内网主机名或 IP如internal-db、10.0.0.86.2 API Token 和 Access KeyLLM Agent 在处理外部服务时可能需要在请求头里携带服务方 Token。推理轨迹如果记录在准备请求阶段很容易把完整请求头内容带到日志里。典型特征以sk-、Bearer、Basic开头的内容云厂商标准 Access Key ID 格式出现在headers、authorization、api_key字段附近6.3 系统提示词推理轨迹经常包含模型对系统提示词的复述和拆解。当公开仓库出现完整 system prompt 时安全团队设计的防护规则就等于全部公开。典型特征文件中出现system prompt、instructions、rules等字段内容与代码仓库中prompts/目录高度相似多处重复的拒绝策略和输出约束6.4 内部 API 结构推理轨迹中会记录工具名称、参数结构、请求 URL。攻击者不需要逆向代码直接看泄露的推理轨迹就能了解系统调用链。典型特征出现tool_name、call_function、parameters字段URL 路径包含内部服务名如/internal/order/query参数名与业务代码强相关6.5 最小泄露案例模拟下面是一个更贴近真实 Agent 开发的泄露案例。假设一个订单查询 Agent 在推理时拼接了内部服务地址和密钥Thought: 用户想查订单状态。我需要调用 query_order 工具。接口地址是 https://internal-gateway.example.com/order/query需要携带 tokenmsy_token_2024。我已经从环境变量读取了配置现在开始构造请求。这段内容如果进入日志文件并提交到仓库攻击者可以直接复用 token 和内部地址发起请求。即使 token 只在内网有效泄露后内网边界也可能被突破。7. 常见问题排查扫描不生效或漏报7.1 扫描器没有匹配到任何内容可能原因项目使用的推理字段名不在规则覆盖范围内字段名被转义或拆行仓库中根本没有推理轨迹只是普通 JSON扫描器默认跳过了.jsonl等扩展名检查方式在仓库里手动执行grep -r reasoning .确认字段名。查看 Aileaks 支持的规则列表确认字段名是否被覆盖。用实验文件验证扫描器本身工作正常。处理建议如果字段名是自定义的需要扩展规则如果扫描器支持自定义规则文件可以手动添加。7.2 大量误报无法定位真正问题可能原因仓库里包含大量测试样本和示例代码敏感特征正则过宽例如password匹配了 YAML 里的password: your_password占位符忽略配置没有生效处理建议先使用排除规则过滤测试和示例目录再使用confidence字段过滤低置信度结果。真正的做法是让仓库示例代码统一使用占位符而不是真实格式的值。7.3 历史提交中的泄露无法修复很多人在当前版本清理了密钥但忽略了 Git 历史。Git 历史中的提交依然可以通过git log找到原始内容。处理方式如果仓库还没有对外公开直接使用git filter-repo重写历史。如果仓库已经公开要立即吊销泄露的凭据。不要试图手动删除大文件来清理历史应该使用专门的工具。清理完成后强制推送并通知所有协作者重新克隆。7.4 扫描导致 CI 耗时过长如果仓库很大全量扫描在每次 MR 中执行会拖慢流水线。改进方式是只扫描本次变更文件或只扫描高优先级路径。find ${CHANGED_FILES} -type f \( -name *.json -o -name *.jsonl -o -name *.log -o -name *.py \) | xargs aileaks scan如果仓库规模很大可以先在本地执行全量扫描生成基线文件CI 中只对比新增命中。基线文件也要做权限控制避免本身泄露。8. 最佳实践如何防止推理轨迹进入仓库8.1 日志采样时主动脱敏在写入推理轨迹日志前先执行脱敏处理。脱敏包括字段级脱敏和内容级脱敏。字段级脱敏是把password、token等字段的值替换为***。内容级脱敏是对长 URL、内网 IP、证书指纹等做正则替换。import re SENSITIVE_FIELD_NAMES { password, passwd, pwd, secret, token, api_key, access_key, authorization } def sanitize_trace(trace: dict) - dict: result {} for key, value in trace.items(): if isinstance(value, dict): result[key] sanitize_trace(value) elif isinstance(value, list): result[key] [sanitize_trace(item) if isinstance(item, dict) else item for item in value] elif key.lower() in SENSITIVE_FIELD_NAMES: result[key] *** elif isinstance(value, str): result[key] re.sub(r(jdbc:\w://[^\s]), jdbc:***, value) else: result[key] value return result实际项目中脱敏逻辑要覆盖所有 Agent 框架的数据模型建议放在序列化入口统一处理。8.2 禁止在默认日志级别打印请求响应生产环境中将 LLM 的完整请求响应打印到 INFO 级别是最危险的习惯。正确做法是提供专门的TRACE_LLM日志级别默认关闭。示例配置logging: level: com.example.llm.client: WARN com.example.llm.trace: OFF当需要排查问题时临时打开TRACE_LLM问题解决后立即关闭。8.3 示例输出必须造假数据技术文档、测试用例、Notebook 中展示 LLM 输出时不要直接复制真实响应。应该统一使用假造的 request ID、假用户名、假订单号和假 Token。推荐做法所有文档示例统一维护在examples/目录示例数据由脚本生成而不是从日志中复制示例中出现的密钥统一使用xxxxxxxxxxxx工具调用 URL 统一使用https://example.internal8.4 仓库根目录添加扫描配置在仓库根目录提交 Aileaks 的配置文件确保所有开发者使用相同规则。配置文件内容包括要扫描的扩展名要忽略的路径自定义推理字段名自定义敏感规则忽略的占位符值这样可以在团队层面统一扫描标准避免每个开发者使用完全不同的规则。8.5 发布前扫描清单以下清单可直接用于 LLM 应用项目的发布前检查检查项检查方式通过标准仓库全量扫描aileaks scan .无高危命中Git 历史扫描对全分支执行扫描无历史泄露环境变量文件检查.env*是否在.gitignore未提交日志目录检查logs/、debug/已忽略Notebook 输出检查.ipynb输出单元格无真实密钥文档示例检查docs/、examples/全部为占位符测试 fixture检查tests/fixtures无真实数据CI 扫描任务确认流水线已包含扫描步骤配置生效9. 扩展方向从仓库扫描到链路防护9.1 推理轨迹治理需要全链路配合仓库扫描只是最后一道防线。更完整的治理链路应该包括提示词与上下文层面控制敏感数据进入模型上下文的范围调用层控制推理轨迹的采集、传输和存储日志层控制日志输出格式和脱敏逻辑发布层通过 Git 钩子和 CI 检查拦截泄露监控层对高敏感字段设置告警Aileaks 解决的是发布层和仓库存量问题但不能替代调用层的脱敏设计。9.2 与 Agent 框架安全设计结合在开发 LLM Agent 时要明确区分“推理内容”和“最终输出”。推理内容默认不进业务日志、不进用户侧消息、不写入持久化表。如果产品要求展示思考过程也要在展示前做脱敏和审核。以 Spring AI、LangChain、LlamaIndex 等项目为例接入前都要确认框架日志是否默认输出thought字段。如果某个框架版本存在自动打印推理轨迹的行为要升级版本或用日志过滤器显式关闭。9.3 持续扫描加定时巡检单次扫描解决不了持续新增的泄露。建议形成三个层级的制度本地提交前开发者在本地执行扫描拦截拦截低质量泄露MR 阶段CI 扫描新增代码拦截大部分问题每周巡检全量扫描所有分支发现历史遗留和绕过 CI 的问题实施时按团队规模选择个人项目至少保留本地提交前检查。9.4 对待扫描规则的取舍大模型应用项目安全扫描的难点是规则太宽会产生大量误报规则太窄会漏掉真正危险的内容。建议维护两组规则第一组是高置信度规则用于阻断发布。例如明文数据库密码、云厂商 Access Key、标准 API Key 格式。这类规则命中就阻止合并。第二组是低置信度规则用于人工复核。例如包含内网 URL、包含token字段、包含reasoning_trace字段。这类规则命中只告警不阻断。这样可以在保证安全的同时避免安全扫描拖慢开发节奏。10. 给大模型应用开发者的三条核心建议第一把推理轨迹当作和数据库密码同等级的敏感数据来管理。不要因为它只是“模型思考过程”就放松存储和日志要求。事实是推理轨迹往往会成为多个敏感信息集中暴露的载体。第二扫描工具要配合治理流程才能发挥作用。单纯运行一次 Aileaks 扫描并修复掉命中的文件并不能解决团队持续产生泄露的问题。要在代码评审规范、日志规范、示例文档规范和 CI 流程中同时落实要求。第三从源头减少敏感数据进入模型上下文。不要在系统提示词中写完整数据库密码不要用固定 Token 做演示不要在大模型请求的上下文里塞进不必要的高敏感字段。上游少一点敏感信息下游泄露风险就低一层。如果项目刚刚引入大模型能力建议先按照本文的实验仓库方式跑一遍扫描识别当前仓库中有没有已经存在的推理轨迹泄露。然后再把扫描接进 CI最后再把脱敏逻辑落到日志入口。按这个顺序推进不需要一次做到完美但每一步都能降低真实风险。