Agent 的输入就是攻击面:GitHub 工作流如何被一条 Issue 反向利用챕터1×0:08开场0:51事件细节2:00技术拆解3:35工程意义4:56落地建议6:05片尾0:006:330:08主持人今天先讲一个很具体的安全事件。有人只需要在一家公司的公开 GitHub 仓库里开一条 Issue,就可能诱导这家公司的 Agent 工作流去读取同组织私有仓库里的内容,再把结果公开写回评论区。这个技术被 Noma Security 命名为 GitLost,七月七日前后公开披露,针对的是 GitHub Agentic Workflows 这类把自然语言、GitHub Actions 和编码 Agent 接在一起的系统。 它最值得工程团队关注的地方,不是「模型被一句话骗了」这么简单,而是整条循环的边界没有分清:Issue 是外部输入,Agent 拿着组织级读取权限,最后又有一个公开输出通道。三件事连在一起,问题就从提示词质量变成了权限和数据流设计。0:51主持人先把攻击链说清楚。SecurityWeek 对披露的复述是:目标工作流监听 Issue 事件,读取标题和正文,再发表评论;它同时能读取组织里的公开和私有仓库。研究者构造了一条看起来像销售负责人提出的正常请求,要求 Agent 去抓取多个仓库的 README。Agent 把 Issue 里的内容当成了任务指令,读取了私有仓库,再把结果放进了公开评论。 这里有两个细节很关键。第一,攻击者不需要私有仓库权限,也不需要代码执行能力,只要能在公开仓库开 Issue。第二,研究者还发现,原本用于拦截这类请求的提示词防护,可以被一个很小的文字变化绕过,前面加上「Additionally」就改变了模型的响应。这个现象说明,基于模型判断的拒答不是权限边界,更不是数据出口的最后一道门。 目前可确认的是研究者的复现实验和披露,不是一次已经被证实的真实组织数据泄露。GitHub Agentic Workflows 官方文档也明确写着它仍处于 Public Preview,并承认 Agent 可能受到提示词注入、恶意仓库内容和被攻陷工具的操纵。2:00主持人把它画成一条 Agent 循环,大致是这样:Issue 进入上下文,模型决定下一步,工具读取仓库,模型整理结果,safe outputs 再把动作写回 GitHub。很多团队看到 safe outputs,会以为危险已经被挡住了。可 safe outputs 主要约束的是「模型想写什么」,不一定能约束「模型先读了什么」,也不一定能阻止它把读到的内容编码进一个看似合法的评论里。 GitHub 官方文档给出的分层设计其实是对的:只读令牌、不把密钥放进 Agent 运行时、沙箱和网络防火墙、safe outputs、威胁检测,以及编译期校验。GitHub 七月九日介绍 Aspire 跨仓库文档工作流时,也展示了类似的结构:Agent 只表达想创建什么,另一个权限更窄的作业去真正创建草稿 PR,而且只允许特定仓库、分支和文件范围。 问题在于,安全控制必须覆盖整条数据流。一个 Agent 如果能读私有仓库,又能读公开 Issue,还能把文字发到公开位置,那么它面对的不是单纯的「恶意提示词」,而是一条潜在的读、推理、写出通道。只把写操作放进闸门,无法自动证明读到的数据不会被带进输出。 这也是 Agent 系统和传统脚本的差别。脚本的输入、变量和副作用通常是开发者显式连起来的;Agent 的上下文会把 Issue、PR、代码、文档和工具返回值混在一起,模型再决定哪些内容是事实,哪些内容像指令。对模型来说,这些文本都在同一个上下文里。对安全设计来说,它们必须分级。3:35主持人第一,权限要按循环中的每一步拆开,而不是给整个 Agent 一个「仓库读权限」。需要读取什么仓库、什么路径、什么事件触发,都应该是 allow-list。跨仓库工作流尤其要小心,产品仓库和文档仓库可以各自授权,但不应该顺手拿到组织里所有仓库的读取能力。 第二,公开输入要和内部指令分区。Issue 标题、正文、评论、PR 描述和代码文件都应该被视为不可信数据,哪怕它们看起来像维护者写的。工程上可以把它们先转成结构化字段,再交给 Agent;更重要的是,系统提示和外部内容要有明确的边界,不能让一段自然语言轻易改变工具权限。 第三,输出闸门要检查数据敏感性,不只是检查动作格式。创建一个草稿 Issue 或 PR,动作本身可能完全合规,但正文里也许包含了私有代码、密钥片段或内部路径。这里需要 DLP、敏感字段扫描、目标可见性检查,必要时只允许输出摘要和引用,不允许原文回显。 第四,默认加入人审,而且人审要发生在公开发布之前。GitHub 在 Aspire 的案例里把文档 PR 设为 draft,由实际提交功能的工程师审阅,Agent 不自动合并。这不是把 Agent 变慢,而是把不可逆的公开写入放到一个人可以看懂的边界上。4:56主持人如果团队现在已经有类似工作流,我建议今天就做一个小审计。沿着每个触发器问四个问题:外部用户能不能控制输入?Agent 能读到哪些私有数据?它能把内容写到哪里?写出之前有没有检查内容本身,而不只是检查 API 动作?只要这四个问题里有一个答案含糊,就先把读取范围和公开输出收紧。 一个更稳的最小版本是:公开 Issue 只触发分类,不直接触发跨仓库读取;Agent 先输出结构化的意图和引用,不输出原文;下游作业再用短期、最小范围的身份访问指定路径;最终生成草稿,交给维护者确认。这样做会牺牲一点自动化的顺滑度,但把循环拆成了几个可以分别审计的阶段。 最后留一个判断标准。不要只问「这个 Agent 会不会被提示词注入」,因为答案几乎总是会。应该问:即使它被一条恶意 Issue 带偏,它最多能读到什么,最多能写出什么,多久能被发现,权限能不能立刻撤掉。只要系统能把这四个上限说清楚,Agent 才是在可运营的边界里工作。6:05主持人GitLost 把一个抽象的安全提醒变成了具体的数据流问题:不可信输入、敏感读取和公开输出,不能同时默认放开。下一次给 Agent 增加一个工具之前,先把它放回这条循环里,看清它会读什么、写什么,以及谁来按下停止键。