随着大型语言模型(LLM)越来越多地生成可运行代码,并被集成到开发流水线与代理(agent)栈中,隐藏或恶意指令的风险正在上升——无论这些指令是嵌入在模型输出中、通过网页或第三方插件注入,还是在模型训练过程中引入——一旦这些代码被执行,可能导致不安全行为。
据开发者社区的用户报告,一名软件开发者在使用CursorAI IDE、借助Gemini 3生成的代码时遭遇灾难性数据丢失——约800GB 的文件被删除,包括CursorAI 应用本体。随着开发者愈发依赖 LLM 进行代码生成,未审查或不安全脚本带来的后果愈发严重。
因此,了解如何检测并移除由 LLM 生成的危险代码至关重要。
在 ChatGPT 与 LLM 语境中,“隐藏代码”是什么?
人们所说的“隐藏代码”指什么?
“隐藏代码”是开发者的统称,用来描述 LLM 摄取或输出的文本(或文件)中包含的任何嵌入式指令或可执行内容,包括:
- 嵌入在用户内容中的“提示式指令”(例如,被隐藏在 PDF 中的“忽略先前指令……”)。
- 用于隐藏标记或打破分词假设的不可见字符或零宽字符。
- 编码载荷(base64、URL 编码、以及嵌入在图像或文档中的隐写内容)。
- 隐藏的 HTML/JS 或脚本块,可能被下游渲染器解释执行。
- 指导检索系统或模型的元数据或注释(文件注释、PDF 隐藏层等)。
- 由于使用危险 API 而产生的隐式行为(例如
eval、exec、subprocess或网络/系统调用),即使并非显式恶意。 - 由提示注入导致的指令,使模型生成包含隐藏命令或类似后门逻辑的代码,因为攻击者对提示或上下文进行了工程化处理。
当目标是改变模型行为时,这些攻击向量通常被称为“提示注入”或“间接提示注入”。安全社区现已将提示注入视为核心 LLM 漏洞,OWASP 也已将其正式归类为 LLM 风险类别。
这与常规恶意软件或 XSS 有何不同?
差异在于语义层:提示注入针对的是模型的指令跟随行为,而非宿主操作系统或浏览器渲染引擎本身。尽管如此,最终在网页渲染器中执行的隐藏 HTML 或脚本仍然是可执行攻击(类似 XSS);语义层与执行层都必须防御。行业领袖与研究者将提示注入称为“前沿安全挑战”,并持续发布缓解策略。
为什么 LLM 会产生隐藏或危险代码?
模型行为、训练数据与指令上下文
LLM 被训练为根据上下文与指令生成看似合理的续写。如果上下文包含对抗性线索,或用户请求模型编写具有强大副作用的代码,模型可能输出包含微妙或主动危险行为的代码。
LLM 会生成看似合理但不安全的代码
LLM 优化的目标是流畅与有用,而非在具有破坏性副作用的情境下确保安全。当被要求“清理”时,它可能轻易生成简洁的 rm -rf /path/to/dir 或 shutil.rmtree(),而且由于响应通常自信措辞,用户可能在缺乏足够审查的情况下复制并运行。这种“自信型幻觉”问题使看似无害的请求也会变得危险。
混淆工作流的自动化
威胁行为者正在通过串联 LLM 调用来自动化代码混淆:一个模型生成载荷,另一个模型重写以规避特征检测,依此类推。2025 年的行业威胁报告与厂商分析已将这种“AI 辅助混淆”记录为新兴技术。
如何检测模型输出中的隐藏代码?
快速排查清单
- 扫描不可见/异常 Unicode(零宽连接符、零宽空格、字节序标记、非 ASCII 同形字符)。
- 运行静态分析/AST 解析,识别强权限 API 的使用(
eval、exec、subprocess、os.system、反射调用)。 - 查找编码载荷(base64、十六进制大块、重复的长字符串或压缩内容)。
- 检查混淆模式(字符串拼接构造 API 名称、字符运算、
chr()链)。 - 使用语义分析确认代码是否实际执行 I/O、联网或文件系统修改。
静态模式检测(快速、第一道防线)
- 具备语言感知的解析与代码规范检查。立即将生成内容区分为代码块与正文。运行格式化与 linter(Black/Prettier、pylint、eslint)。规则应标记
eval、exec、rm -rf、原始子进程调用或动态构造的 shell 管道。 - 基于标记与字符串的模式扫描器。搜索高风险标记与模式:
sudo、如/home/、C:\等绝对路径,rm -rf、shutil.rmtree、subprocess.Popen、内联 base64 大块、无法直观理解的长字符串、以及切换解释器上下文的 shebang。 - 密钥扫描与来源校验。检测硬编码凭据、指向不受信注册表的 URL、或动态从任意来源拉取包的代码。
静态分析能快速捕获许多显而易见的问题,并且作为 CI 关卡运行成本低。
语义与上下文检测(更深入)
- 意图分析。使用次级模型或规则引擎对生成代码的意图进行分类:是“读”“写”“删除”“网络”“安装”?凡是被归类为删除/写入,应升级处理。
- 数据流分析。分析代码,检测未经验证或来源于用户输入的路径是否可达破坏性 API。例如,若某变量源自 LLM 输出或远程文件,并被拼接进 shell 命令,应当标记。
- 溯源关联。保留对话、系统提示与上下文页面的完整记录。若可疑输出与某个外部文档或插件调用相关,则可能指示提示注入或被污染的上下文。
动态与行为检测(最可靠)
- 带监控的沙箱执行。在严格受限、一次性的环境中执行生成代码,禁用网络、禁止挂载主机,并使用系统调用过滤(seccomp)。监控文件系统活动、网络调用尝试、进程生成与异常 I/O。
- 金丝雀测试。在真实数据运行前,先对合成目录(包含哨兵文件)运行;监控删除或覆盖行为。
- 行为启发式。关注遍历父目录的循环、无深度检查的递归操作、或可能伤害大量文件的重命名模式(例如反复写入同一文件名)。
动态分析是检测仅在运行时触发、延时或经过混淆的载荷的唯一方式。
在执行 LLM 输出前,应如何移除或中和隐藏代码?
防御性移除 vs. 改变语义
在“移除隐藏代码”时有两个目标:
- 净化(Sanitization)——移除显然非代码或可疑内容(不可见 Unicode、零宽字符、附加的 base64 载荷)。这不应改变本意的良性逻辑。
- 中和(Neutralization)——对于任何会执行或调用外部服务的代码,在验证前将这些调用禁用或替换为无操作。
始终优先考虑“中和 + 复核”,而非盲删:随意删除代码会导致破损或意外行为。应以显式、记录在案的桩替换可疑结构,安全失败(抛出异常或返回安全默认值)。
第 1 步——将生成代码视为不受信数据
切勿直接执行来自 ChatGPT(或任何 LLM)的代码,必须先经过移除与加固流水线。该流水线应由策略强制并在 CI/CD 中自动化。
第 2 步——提取并规范化代码
- 文本规范化并移除零宽字符:剔除 U+200B、U+200C、U+200D、U+FEFF 等零宽/格式化码点。将移除内容记录到审计日志。此步骤能消除许多用于视觉隐匿的“隐藏”编码。
- 剥离所有非代码上下文:移除叙述性文字、隐藏注释与任何 HTML/Markdown 包裹。用语言格式化器(Black、Prettier)将代码转换为规范形式,归一化混淆空白或控制字符。
- 拒绝或隔离包含以下结构的代码:动态
eval、原始子进程调用(os.system、subprocess.Popen)、内联 base64 载荷并解码执行、或试图切换解释器上下文的嵌入式#!指令。文本规范化并移除零宽字符
剔除 U+200B、U+200C、U+200D、U+FEFF 等零宽/格式化码点。将移除内容记录到审计日志。此步骤能消除许多用于视觉隐匿的“隐藏”编码。
第 3 步——解析为 AST 并替换高风险节点
将代码解析为 AST,查找调用动态执行(如 exec)或以编程方式构造函数名的节点。用安全桩替换它们,使其抛出“已阻止不安全动态行为”的受控异常。生成基于 AST 的净化源码供审查。运行安全模式检查(为你的环境定制 semgrep 规则)。匹配命中处进行标记与中和。
第 4 步——静态加固与重写
- 自动重写:通过自动净化器将危险调用替换为安全封装器——例如用受控沙盒执行器替换
os.system()/subprocess,强制超时与网络阻断。 - 能力门控:修改或移除 API 密钥、令牌或对高权限端点的调用;在本地测试中改为使用模拟适配器。防止意外包含机密或 URL。
- 依赖重写:阻止代码内动态执行
pip/npm安装。要求依赖在你的制品库中声明并审批。
第 5 步——在高强度沙箱中运行
- 一次性容器/微型虚机:在无网络、无法访问主机凭据、文件系统访问受限的容器/虚机中执行。可使用 gVisor、Firecracker 或专用的一次性执行服务。若必须访问 I/O,通过策略执行的代理进行。
- 系统调用过滤与 seccomp:限制允许的系统调用。阻止临时目录之外的文件写入。
- 资源/时间限制:设置 CPU/内存/时间限制,避免逻辑炸弹无限运行。
沙箱执行与监控常能发现静态检查遗漏的载荷。行业指南与近期白皮书均建议将沙箱作为核心缓解手段。
你的流水线应包含哪些自动化工具与规则?
推荐工具链组件
- Unicode 净化模块(自研或现有库)。必须记录被规范化的字符。
- 各目标语言的解析器与 AST 分析器(Python 的
ast、typed-ast,JavaScript 解析器,Java 解析器)。 - 静态分析/SAST:Bandit(Python)、Semgrep(多语言、可定制)、带安全插件的 ESLint。
- 熵与解码启发式:检测 base64/hex/gzip 并路由至检查。
- 沙箱运行时:极简容器,使用严格的 seccomp/AppArmor 配置,或在禁用系统调用的语言级解释器中运行。
- 策略执行器:决定允许的模块、端点与安全 API 封装器。
- 审计追踪:不可篡改的日志,记录原始输出、净化后输出、差异与决策。
示例 semgrep 模式(概念)
使用简短而保守的规则标记危险函数的使用。例如:
- 标记
eval、exec、Function构造器(JS)、动态导入或字符串构造的 API 名称。 - 标记超出允许列表的网络调用(例如未知主机上的
requests.get)。 - 标记对敏感路径(如
/etc、系统文件夹)的写入,以及进程生成。
(将这些作为组织的配置项,并随时间逐步收紧。)
有哪些实用的净化代码片段(安全示例)?
以下是可复用的、非危险的防御性示例。它们是净化与检测片段——而非利用性指令。
示例:移除零宽字符(Python,防御性)
import re
ZERO_WIDTH_RE = re.compile(r'')
def strip_zero_width(s: str) -> str:
cleaned = ZERO_WIDTH_RE.sub('', s)
return cleaned
这会移除攻击者常用来在可见文本中隐藏代码的字符。务必记录移除了哪些内容,并将其纳入审计轨迹。
示例:解析并检查 AST(Python,概念)
import ast
def has_dynamic_exec(source: str) -> bool:
tree = ast.parse(source)
for node in ast.walk(tree):
if isinstance(node, ast.Call):
if getattr(node.func, 'id', '') in ('eval', 'exec',):
return True
if isinstance(node, ast.Attribute):
if getattr(node, 'attr', '') in ('popen', 'system'):
return True
return False
如果 has_dynamic_exec 返回 True,请勿运行代码;而应将动态节点替换为安全桩并要求人工复核。
注:这些示例具有防御性质。不要移除日志、审计或人工复核环节。
结语:始终将 LLM 输出视为不受信代码
语言模型是强大的生产力工具——它们可以产出优雅代码、加速草稿与自动化日常工作。但一旦触及执行边界,安全规则就改变了:模型输出必须被视为不受信任的工件。过去 18–30 个月里提示注入、后门研究与真实世界披露的结合清楚地表明:风险面正在扩大,并将持续演化。
结合解析、静态分析、沙箱化动态测试、治理与持续红队的实用防御,足以阻止大多数攻击。但团队也必须投入组织层面的控制:最小权限、来源可溯与“默认需要验证”的文化。行业正在构建工具与框架以简化这些模式;在此期间,采用上述清单可降低隐藏载荷漏网的概率。
开发者可以通过 CometAPI 访问最新的 LLM API,例如Claude Sonnet 4.5 API与Gemini 3 Pro Preview等;最新模型版本会与官网保持同步更新。开始之前,可在Playground探索模型能力,并查阅API guide获取详细指引。访问前请确保已登录 CometAPI 并获取 API key。CometAPI提供远低于官网的价格,帮助你完成集成。
Ready to Go?→ Sign up for CometAPI today !
