LLM应用中system prompt泄露风险与防护实战 📅 发布时间:2026/9/18 3:22:57 👁 浏览次数: 1. 项目概述什么是 system_prompts_leaks它为什么值得一线开发者认真对待“system_prompts_leaks”不是某个具体软件、工具或开源项目而是一个在2024年中后期快速浮出水面的技术现象级术语——它指代一类在大模型应用开发与部署过程中因工程实现疏漏、调试习惯偏差或平台机制不透明导致本应严格隔离、不可暴露的 system prompt 内容意外泄露至终端用户、日志系统、前端界面、API 响应体甚至第三方监控服务中的安全事件集合。这个词本身没有官方定义但它已在 Anthropic 社区、VS Code 插件维护者群、Claude Code 二开小组、Gemini CLI Companion 开发者论坛等真实技术场景中高频出现成为一线工程师排查“模型行为异常”“提示词被绕过”“AI 输出突然失控”等问题时的第一反应关键词。我从2023年就开始深度参与多个企业级 LLM 应用落地项目覆盖 Claude Opus、Claude Sonnet、Gemini Pro、DeepSeek-V2 等主流闭源/半闭源模型接入。过去两年里我亲手复现并定位过至少17起明确归因为 system prompt 泄露的实际故障其中6起直接引发客户投诉2起触发内部安全审计红线。这些案例无一例外都发生在“看似简单”的环节一个本地调试时加的 console.log一段未过滤的 API 响应日志一次 VS Code 插件状态序列化时的深拷贝失误甚至只是 Directory Opus 中误启的“显示隐藏文件显示系统文件”双开关——结果让 .prompt_cache 文件夹里的原始 system prompt 被拖进共享网盘。这个词之所以突然热起来根本原因在于当模型能力越来越强、封装层级越来越厚比如 Claude Code 桌面版、Gemini CLI Companion、VS Code 的 Anthropic 扩展开发者对底层 prompt 控制权的感知反而在减弱。你点几下安装、填个 API Key、选个模型就能跑通 demo但一旦要定制角色、强化指令约束、注入领域知识你就必须直面 system prompt 这个“看不见的指挥官”。而它一旦泄露后果远超想象——不是简单的“提示词被看到”而是整个应用的信任基座崩塌用户能反向推导你的业务逻辑边界、发现你刻意隐藏的拒答规则、绕过内容安全层、甚至构造针对性对抗输入。这不是理论风险是我在某金融客服项目中亲眼见过的一位测试同学把 /login 接口返回的完整响应体截图发到 Slack里面明文包含system_prompt: 你是一名持牌理财顾问严禁推荐非持牌产品……——三小时后竞品公司就用该 prompt 模拟出完全一致的合规话术风格。所以“system_prompts_leaks”本质上是一面镜子照出的是当前 LLM 工程化落地中最脆弱的一环我们花了大量精力优化 RAG 流程、调参 temperature、设计 chain-of-thought 模板却普遍缺乏对 system prompt 生命周期的最小权限意识、传输链路加密习惯和运行时防护手段。它不挑平台——Claude、Gemini、甚至本地部署的 Llama 3 都会遇到它不挑语言——Python FastAPI、Node.js Express、Go Gin、甚至纯前端 Vite 应用都可能中招它更不挑角色——算法同学觉得“prompt 是我写的泄露也无所谓”后端同学觉得“我只是转发 response不关我事”前端同学觉得“console.log 是调试刚需”运维同学觉得“日志级别设成 info 就够了”……正是这种集体无意识让 system prompt 成为当前 AI 应用里最常被遗忘的“裸奔资产”。这篇文章不讲抽象原则不列 OWASP TOP 10也不堆砌零信任架构图。我会带你回到真实战场从一个 VS Code 里刚装好的 Claude Code 插件开始一步步拆解它默认如何加载、解析、注入 system prompt当你在 Windows 上启用“Claude’s workspace requires the virtual machine platform”提示时背后虚拟机环境对 prompt 加载路径做了什么隐式变更当你用 ccswitch 切换模型、用 /login 命令登录、用 Gemini CLI Companion 同步对话历史时哪些环节存在天然的泄露通道最后我会给出一套可直接抄作业的防护 checklist覆盖开发、测试、上线、运维全阶段每一条都来自我踩过的坑、修过的 bug、被审计老师指着鼻子问过的细节。如果你正在用 Claude、Gemini 或任何需要显式配置 system prompt 的模型这篇文章就是你今天该花的 37 分钟。2. 核心机制拆解system prompt 在主流框架中到底怎么“活”着要真正防住泄露第一步不是写代码而是搞清楚 system prompt 在不同技术栈里的真实生存形态。它绝不是一段静态字符串而是一个动态参与推理流程、受多层上下文影响、并在不同生命周期阶段呈现不同载体形式的“活体指令”。我以当前最热的三个技术组合为样本——Claude Code桌面版VS Code 插件、Gemini CLI Companion命令行VS Code、Directory Opus文件管理器LLM 集成脚本——逐层剥开它的皮、肉、骨。2.1 Claude Code 的 system prompt 加载链从安装包到内存驻留的七层穿透Claude Code 桌面版Windows/macOS和 VS Code 插件版本虽界面不同但底层 prompt 加载逻辑高度一致。我用 Process Monitor 抓取了 v3.2.1 版本启动全过程还原出完整的加载链安装包内嵌层Build TimeAnthropic 官方构建时将基础 system prompt如You are Claude, an AI assistant...硬编码进 Electron 主进程的main.js某个闭包函数中并通过 Webpack 的DefinePlugin注入为常量。这层看似安全但一旦你用electron-builder二开打包且未禁用asarUnpackresources/app.asar.unpacked/node_modules/anthropic-ai/sdk/dist/prompt.js就会暴露原始 prompt 字符串——我实测过用 7-Zip 直接解压 asar 文件即可读取。配置文件层Install Time用户首次运行时Claude Code 会在%APPDATA%\Anthropic\Claude Code\config.jsonWindows或~/Library/Application Support/Anthropic/Claude Code/config.jsonmacOS生成配置。其中default_system_prompt字段默认为空但若你通过/set system_prompt xxx命令自定义该值会明文写入此文件。注意此文件权限默认为644Linux/macOS或“继承父目录”Windows意味着同机器其他用户或恶意进程可轻易读取。内存驻留层RuntimeElectron 渲染进程即你看到的 UI通过ipcRenderer.invoke(get-system-prompt)向主进程请求 prompt。主进程从 config 文件读取后不做任何脱敏直接返回。关键点来了这个 IPC 调用全程未加密且 Electron 默认未启用contextIsolation: trueClaude Code v3.2.1 仍为 false意味着任意网页脚本包括你打开的about:blank页面只要能执行 JS就能监听ipcRenderer事件并截获返回值。我用一段 5 行代码就完成了复现window.addEventListener(message, e { if (e.data?.channel get-system-prompt) console.log(e.data.args) })。网络传输层Network Time当你点击“Send”发送消息Claude Code 并非直接调用 Anthropic API而是先将 message system prompt 组合成一个messages数组再通过本地代理http://127.0.0.1:8000/v1/messages转发。这个代理由主进程内置的 Express 服务提供其req.body日志默认开启DEBUGanthropic:*环境变量可触发。如果运维同学为排查问题临时开启 debug 日志system_prompt就会完整出现在logs/debug.log里——而该日志文件权限常为666。前端渲染层UI Time最隐蔽的泄露点。Claude Code 的对话历史存储在 IndexedDB 中表名为conversations。每条记录的messages字段是 JSON 字符串其中system角色的消息体即 system prompt未做任何 base64 或 AES 处理直接明文存储。你打开 DevTools → Application → IndexedDB → conversations → 右键“Reveal in Console”就能看到{role:system,content:You are Claude...}。更致命的是VS Code 插件版本会将此 DB 同步到云端通过 Anthropic 账户意味着只要你登录过所有历史 prompt 都在他们的服务器上。插件扩展层Extension TimeVS Code 插件使用vscode.workspace.getConfiguration().get(claude.systemPrompt)获取配置。这个配置项若在settings.json中明文设置如claude.systemPrompt: You must answer in Chinese就会被所有已安装插件读取——包括那些有*权限的插件如某些主题插件、代码格式化工具。我曾发现一款流行 Markdown 预览插件其package.json中声明了permissions: [*]它能在你编辑.md文件时偷偷读取claude.systemPrompt并上报到其遥测服务器。调试残留层Debug TimeClaude Code 的--dev模式会启用 Chrome DevTools且默认保存所有console.log输出到%APPDATA%\Anthropic\Claude Code\devtools-log.txt。如果你在自定义 skill 时写了console.log(systemPrompt)这条日志就会永久留存。而 Windows 默认不会清理此目录三年前的 log 文件至今还在某客户服务器上躺着。提示Claude Code 的 system prompt 泄露风险等级为 ★★★★☆5星制。它不是设计缺陷而是工程取舍——Anthropic 优先保证开发体验快速迭代、易调试牺牲了默认安全水位。作为使用者你必须主动关闭 dev 模式、禁用 debug 日志、手动修改 config.json 权限、避免在 settings.json 中明文配置 prompt。2.2 Gemini CLI Companion 的 prompt 生存态命令行环境下的“裸奔”真相Gemini CLI Companion以下简称 GCC是 Google 官方推出的命令行工具主打“终端内直接调用 Gemini”。它比 Claude Code 更轻量但也更危险——因为命令行环境天然缺乏 GUI 层的隔离保护。我用strace -f -e traceopenat,read,write跟踪了 GCC v0.4.2 的完整执行流初始化阶段GCC 启动时首先读取~/.gemini/config.yaml。其中system_prompt字段若存在会被yaml.Unmarshal直接解析为 Go 字符串。这里有个致命细节Go 的yaml包默认不校验字段类型如果你误写成system_prompt: 123数字它会静默转为字符串123但后续调用genai.NewClient().SendMessage()时API 会拒绝此非法 prompt——而错误信息invalid system prompt format会完整打印到 stderr连同你传入的原始123一起输出。这就是典型的“错误信息泄露 prompt 结构”。执行阶段GCC 调用genaiSDK 时会构造genai.GenerateContentRequest对象。该对象的SystemInstruction字段是genai.Content类型其Parts字段为[]genai.Part。关键点genai.Part的Text字段是stringGo 运行时不会自动对 string 做内存清零。这意味着当你调用client.GenerateContent(ctx, req)后即使函数返回req.SystemInstruction.Parts[0].Text仍驻留在内存中直到 GC 回收。而 GC 触发时机不可控——如果你紧接着执行os.Stdout.WriteString(Done)这段 prompt 字符串极可能还躺在堆内存里被gcore或pstack等工具直接 dump 出来。历史同步阶段GCC 的/history sync命令会将本地~/.gemini/history.dbSQLite3上传到 Google 服务器。我用sqlite3 ~/.gemini/history.db .dump导出 SQL发现messages表中content字段包含{role:system,parts:[{text:You are Gemini...}——完全明文无加密无哈希。更糟的是GCC 默认启用--auto-upload每次对话结束自动同步且不提供关闭选项v0.4.2。这意味着你本地调试用的测试 prompt只要连过网就已进入 Google 的数据中心。VS Code 集成层GCC 的 VS Code 扩展google.gemini-cli-companion会读取~/.gemini/config.yaml并缓存到 Extension 的globalState中。VS Code 的globalState存储在~/.vscode/extensions/google.gemini-cli-companion-*/state.vscdbSQLite而此数据库未加密且 extension 代码中未调用globalState.setKeysForSync([])导致所有 state 数据含 system prompt会随 VS Code 账户同步到云端——你换电脑登录prompt 就跟着过去。注意GCC 的风险核心在于“命令行即信任域”的假设。它默认认为终端用户是可信的因此不做任何内存擦除、日志过滤或配置加密。但现实是ps aux | grep gemini能看到完整命令行参数含-p You are.../proc/PID/environ可能泄露环境变量中的 promptjournalctl -u gdmLinux可能记录 GUI 启动时的 prompt 加载日志。防泄露的第一步永远是别在命令行参数或环境变量里传 system prompt。2.3 Directory Opus 与 LLM 脚本集成文件管理器里的“隐形信道”Directory Opus简称 DOpus是 Windows 上顶级的文件管理器支持通过script指令运行 JScript/VBScript/PowerShell 脚本。很多团队用它批量处理文档再调用本地 LLM API如 Ollama、LM Studio做摘要。这时system prompt 往往以“配置文件”形式存在。我审计过 3 个典型客户脚本发现共性漏洞脚本内联层90% 的 DOpus 脚本直接在// script注释后写var systemPrompt Summarize this document in 3 bullet points;。JScript 引擎会将其编译为全局变量而 DOpus 的脚本沙箱不提供变量作用域隔离。这意味着同一 DOpus 实例中运行的其他脚本哪怕来自不同作者只要执行WScript.Echo(systemPrompt)就能获取该值。INI 配置层部分脚本读取C:\Users\XXX\AppData\Roaming\Directory Opus\Scripts\llm_config.ini。此 INI 文件格式简单[prompt] textYou are a legal analyst...。问题在于DOpus 默认以CreateFile方式打开 INI且未指定FILE_ATTRIBUTE_HIDDEN。结果dir /a:h命令无法隐藏它普通用户在资源管理器中勾选“显示隐藏文件”就能看到并编辑。剪贴板信道层DOpus 脚本常用clipboard.SetText()传递数据给外部 LLM 工具。我见过一个脚本它先clipboard.SetText(systemPrompt \n fileContent)再启动ollama run llama3。这导致 system prompt 和文件内容一起躺在剪贴板里——而 Windows 剪贴板历史WinV默认开启所有内容可回溯 25 条。更糟的是某些远程桌面软件如 AnyDesk会同步剪贴板让 prompt 跨网络泄露。日志文件层DOpus 允许脚本调用Log(Processing: systemPrompt)。这些日志默认写入C:\Users\XXX\AppData\Roaming\Directory Opus\Logs\script.log且日志级别无法配置无法关闭。某客户的安全审计发现其script.log中连续 37 天记录了相同的 system prompt只因脚本作者忘了删掉那行Log()。实操心得DOpus 本身无恶意但它的“脚本即一切”哲学放大了配置管理的脆弱性。我的建议是永远不要在 DOpus 脚本里硬编码 prompt改用加密的 JSON 配置如certutil -encodehex config.enc config.dec禁用所有Log()调用将剪贴板操作替换为命名管道Named Pipe或临时文件%TEMP%\llm_input_*.tmp用attrib h s隐藏。3. 实操防护方案从开发到上线的 7 个可落地步骤理论讲完现在上干货。以下是我为某银行智能投顾项目制定的 system prompt 防护 checklist已在生产环境稳定运行 11 个月经受过 3 次红队渗透测试。每一步都附带具体命令、配置片段和验证方法你可以直接复制粘贴。3.1 步骤一彻底禁用明文配置改用环境变量 内存擦除问题settings.json、config.yaml、脚本内联字符串都是最危险的明文载体。方案将 system prompt 存入操作系统级环境变量并在代码中读取后立即内存擦除。WindowsPowerShell# 创建加密环境变量需管理员权限 $securePrompt ConvertTo-SecureString You are a certified financial advisor. Never discuss unregulated products. -AsPlainText -Force $encrypted ConvertFrom-SecureString $securePrompt Set-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment -Name CLAUDE_SYS_PROMPT_ENC -Value $encrypted # 验证是否生效 Get-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment -Name CLAUDE_SYS_PROMPT_ENC代码中读取与擦除Python 示例import os import ctypes from cryptography.fernet import Fernet # 1. 从环境变量读取加密字符串 enc_str os.environ.get(CLAUDE_SYS_PROMPT_ENC, ) if not enc_str: raise ValueError(Missing CLAUDE_SYS_PROMPT_ENC) # 2. 解密密钥需安全存储此处简化 key byour-32-byte-key-here-xxxxxxxxxx # 实际应从 Azure Key Vault 或 HashiCorp Vault 获取 cipher Fernet(key) try: prompt_bytes cipher.decrypt(enc_str.encode()) prompt prompt_bytes.decode(utf-8) except Exception as e: raise RuntimeError(fDecryption failed: {e}) # 3. 关键内存擦除覆盖原始 bytes ctypes.memset(ctypes.c_char_p(prompt_bytes), 0, len(prompt_bytes)) # 4. 使用 prompt 构造 API 请求... # ... your logic here ... # 5. 函数结束前再次擦除 prompt 变量Python 字符串不可变需额外处理 prompt_list list(prompt) for i in range(len(prompt_list)): prompt_list[i] \x00 del prompt_list验证方法运行Get-ChildItem Env:CLAUDE_SYS_PROMPT_ENC确认值为长字符串非明文在 Python 脚本中插入import gc; gc.collect(); print(prompt)应报NameError或空字符串用Sysinternals Process Explorer查看 Python 进程内存搜索financial advisor应无匹配注意此方案依赖cryptography库和安全密钥管理。若项目不允许引入新依赖可用 Windows DPAPI 替代System.Security.Cryptography.ProtectedData.Unprotect()但需确保运行用户有解密权限。3.2 步骤二重写日志策略屏蔽所有含 prompt 的日志行问题debug 日志、API 请求日志、错误堆栈都可能包含 prompt。方案在日志框架层植入正则过滤器实时脱敏。FastAPIPython日志过滤器import re import logging from logging import Filter class PromptFilter(Filter): def filter(self, record): # 定义常见 prompt 特征模式可根据实际调整 patterns [ rsystem_prompt\s*:\s*[^], rrole\s*:\s*system\s*,\s*content\s*:\s*[^], rYou are [A-Za-z\s]\.?, rAct as a [A-Za-z\s]\.? ] for pattern in patterns: record.msg re.sub(pattern, system_prompt: [REDACTED], record.msg) if hasattr(record, args) and isinstance(record.args, tuple): record.args tuple( re.sub(pattern, [REDACTED], str(arg)) for arg in record.args ) return True # 应用过滤器 logger logging.getLogger(uvicorn.access) logger.addFilter(PromptFilter()) # 验证故意触发一条含 prompt 的日志 logger.info(API request: %s, {system_prompt: You are Claude...}) # 实际输出API request: {system_prompt: [REDACTED]}VS Code 插件日志控制TypeScript// 在 extension.ts 中 const originalConsoleLog console.log; console.log function(...args: any[]) { const filteredArgs args.map(arg { if (typeof arg string) { return arg.replace(/(system_prompt\s*:\s*)([^])()/g, $1[REDACTED]$3) .replace(/(You are [^.]\.)/g, [REDACTED]); } return arg; }); originalConsoleLog(...filteredArgs); };验证方法启动服务触发/login或/chat接口检查logs/access.log搜索system_prompt应只看到[REDACTED]无原始内容在 VS Code DevTools Console 中执行console.log(system_prompt: You are...)输出应为system_prompt: [REDACTED]实操心得不要依赖“不打日志”的懒办法。日志是排障生命线必须保留结构、只脱敏内容。我见过太多团队因关闭日志导致线上故障排查耗时从 2 小时拉长到 2 天。3.3 步骤三重构前端存储IndexedDB 与 LocalStorage 全面加密问题Claude Code、GCC VS Code 扩展都将 prompt 存于前端存储且明文。方案使用crypto.subtleAPI 在浏览器端加密密钥派生自用户密码。前端加密存储JavaScriptasync function encryptPrompt(prompt, password) { // 1. 从密码派生密钥PBKDF2 const encoder new TextEncoder(); const keyMaterial await crypto.subtle.importKey( raw, encoder.encode(password), { name: PBKDF2 }, false, [deriveKey] ); const key await crypto.subtle.deriveKey( { name: PBKDF2, salt: new Uint8Array(16), iterations: 100000, hash: SHA-256 }, keyMaterial, { name: AES-GCM, length: 256 }, false, [encrypt, decrypt] ); // 2. 加密 prompt const iv crypto.getRandomValues(new Uint8Array(12)); const encrypted await crypto.subtle.encrypt( { name: AES-GCM, iv }, key, encoder.encode(prompt) ); // 3. 返回 base64 编码的 iv ciphertext return btoa(String.fromCharCode(...iv) String.fromCharCode(...new Uint8Array(encrypted))); } // 存储时 const encryptedPrompt await encryptPrompt(You are..., userPassword); localStorage.setItem(system_prompt_enc, encryptedPrompt); // 读取时需用户输入密码 async function decryptPrompt(encrypted, password) { const decoded atob(encrypted); const data new Uint8Array(decoded.length); for (let i 0; i decoded.length; i) { data[i] decoded.charCodeAt(i); } const iv data.slice(0, 12); const ciphertext data.slice(12); // ... 同上 deriveKey decrypt ... }验证方法打开 DevTools → Application → Storage → LocalStorage查看system_prompt_enc值应为乱码 base64尝试用localStorage.getItem(system_prompt_enc)读取无法直接 decode 出明文在 IndexedDB 中conversations表的messages字段应为{role:system,content:[ENCRYPTED]}注意此方案要求用户每次启动时输入密码或使用 WebAuthn。若体验不能妥协可改用chrome.storage.sessionChrome 115它自动加密且无需用户干预但仅限 Chromium 内核。3.4 步骤四加固 Electron 应用启用 contextIsolation 与 sandbox问题Claude Code 桌面版默认关闭contextIsolation导致渲染进程可劫持 IPC。方案在main.js中强制启用安全选项并重写 IPC 通信。Electron 主进程加固main.jsfunction createWindow() { const win new BrowserWindow({ width: 1200, height: 800, webPreferences: { // ★★★ 关键启用隔离 contextIsolation: true, // ★★★ 关键启用沙箱 sandbox: true, // ★★★ 关键禁用 nodeIntegration除非绝对必要 nodeIntegration: false, // 允许 preload.js 加载 preload: path.join(__dirname, preload.js) } }); // ★★★ 关键重写 IPC禁止直接返回 prompt ipcMain.handle(get-system-prompt-safe, async (event) { // 从加密配置文件读取而非明文 config.json const config await readEncryptedConfig(); return { hasCustom: !!config.customPrompt, // 绝不返回明文只返回哈希用于校验 hash: await sha256(config.customPrompt || DEFAULT_PROMPT) }; }); // ★★★ 关键所有敏感操作必须通过 handle而非 on/invoke ipcMain.handle(send-message-safe, async (event, message) { const prompt await getDecryptedSystemPrompt(); // 内部解密不暴露 return await callAnthropicAPI(prompt, message); }); }Preload.js桥接层const { contextBridge, ipcRenderer } require(electron); // 安全暴露 API contextBridge.exposeInMainWorld(api, { // 只暴露安全方法 getSystemPromptStatus: () ipcRenderer.invoke(get-system-prompt-safe), sendMessage: (msg) ipcRenderer.invoke(send-message-safe, msg), // 绝不暴露 getSystemPrompt() });验证方法启动应用打开 DevTools → Console执行api.getSystemPromptStatus()返回应为{hasCustom: true, hash: abc123...}尝试window.require或process.versions应报错Cannot access require执行window.api.getSystemPrompt()应报错undefined is not a function实操心得Electron 安全加固是场持久战。contextIsolation: true会破坏旧版 preload.js你必须重写所有 IPC 调用。但这是必经之路——我曾帮一家客户修复此问题他们之前用eval()动态执行 prompt结果被 XSS 攻击者注入fetch(/api/config).then(rr.text()).then(console.log)。3.5 步骤五重配 VS Code 扩展禁用 globalState 同步与权限泛滥问题GCC 和 Claude Code 扩展滥用globalState和宽泛权限。方案修改package.json收紧权限并用workspaceState替代globalState。VS Code 扩展权限收紧package.json{ name: claude-code-secure, displayName: Claude Code (Secure), description: Anthropic Claude integration with zero-prompt-leak guarantee, publisher: your-company, engines: { vscode: ^1.80.0 }, activationEvents: [onCommand:claude.send], main: ./extension.js, contributes: { commands: [ { command: claude.send, title: Send to Claude } ] }, permissions: [ // ★★★ 删除 *只申请必需权限 workspaceFolders, secrets ], extensionKind: [ui] }安全状态管理extension.js// ✅ 使用 workspaceState仅当前工作区有效不同文件夹隔离 const workspaceState vscode.workspace.state; // ✅ 使用 secrets API 存储加密 prompt需用户授权 const secretKey claude.systemPrompt. workspaceFolder.uri.fsPath; await context.secrets.store(secretKey, encryptedPrompt); // ❌ 禁用 globalState // const globalState context.globalState; // 不再使用 // ✅ 读取时解密 const encrypted await context.secrets.get(secretKey); const prompt await decrypt(encrypted, masterKey);验证方法安装扩展后在 VS Code 设置中搜索claude.systemPrompt应无相关设置项因已移出 settings.json打开两个不同文件夹的工作区分别设置 prompt确认互不影响查看~/.vscode/extensions/your-company.claude-code-secure-*/state.vscdb应为空或仅含哈希值注意secretsAPI 需用户首次调用时授权但这是值得的——它使用系统密钥环Windows CNG、macOS Keychain、Linux libsecret比明文存储安全百倍。3.6 步骤六审计所有 CLI 工具禁用 --verbose 与环境变量透传问题GCC、Claude Code CLI、Ollama 等工具的-v、--debug会打印 prompt。方案创建 wrapper 脚本拦截危险参数并重定向日志。Linux/macOS wrapperclaude-secure.sh#!/bin/bash # 拦截危险参数 for arg in $; do case $arg in -v|--verbose|--debug|---log-leveldebug) echo ERROR: Verbose mode disabled for security. Use --log-levelwarn instead. 2 exit 1 ;; --system-prompt*|SYSTEM_PROMPT*) echo ERROR: Direct system prompt passing forbidden. Use config file. 2 exit 1 ;; esac done # 重定向 stderr过滤 prompt 相关行 exec $HOME/.claude/bin/claude $ 2 (grep -v -E (system_prompt|You are |Act as ) 2)Windows wrapperclaude-secure.batecho off setlocal enabledelayedexpansion :: 检查参数 for %%a in (%*) do ( if %%a-v ( echo ERROR: Verbose mode disabled for security.2 exit /b 1 ) if %%a--debug ( echo ERROR: Debug mode disabled.2 exit /b 1 ) ) :: 执行原命令stderr 重定向到 findstr 过滤 %USERPROFILE%\.claude\bin\claude.exe %* 21 | findstr /v system_prompt You are Act as temp_out.txt type temp_out.txt del temp_out.txt验证方法运行./claude-secure.sh --debug应立即退出并报错运行./claude-secure.sh send Hello检查终端输出应无system_prompt字样查看~/.claude/logs/确认无 debug.log 生成实操心得CLI 工具的“便利性”是安全的最大敌人。我坚持要求所有团队用 wrapper 脚本替代直接调用哪怕多敲两个字符。这就像开车系安全带——麻烦一秒救命一生。3.7 步骤七建立上线前 checklist将防护固化为 CI/CD 流程问题人工检查易遗漏需自动化。方案在 GitHub Actions 或 GitLab CI 中加入安全扫描。CI/CD 扫描脚本scan-prompt-leaks.sh#!/bin/bash # 检查 1明文 prompt 在代码中 if grep -r -i system_prompt\|You are