我们让 AI 代理运行了 Bash 命令,竟然还活着讲出这个故事 — Sarah Sanders,PostHog
AAI Engineer
Computing/SoftwareSmall Business/StartupsInternet Technology
Transcript
00:00:00大家好,感觉怎么样?我们正处于冲刺阶段。我叫萨拉,是 PostHog 的上下文
00:00:23工程师,非常高兴能每天参与我们深受喜爱的向导工具的开发工作。
00:00:30那么,什么是向导?向导会为你配置 PostHog。它是一个具代理能力的命令行工具,
00:00:39能够读取你的代码库,为你的项目安装正确的 SDK,插桩
00:00:44你的事件,并为你设置仪表板。它把以前需要大约一到两个小时
00:00:52才能完成的设置缩短到大约五到六分钟,而且推理是免费的,由我们提供,
00:00:57确保你能顺畅地接入 PostHog。听起来是不是很酷?大家
00:01:03都很喜欢它。但几个月前,我们开始大胆设想:如果这成为安装 PostHog 到项目的
00:01:10推荐方式或默认方式会怎样?结果我的安全警报立刻拉响了。
00:01:17我开始质疑这东西到底有多安全,因为它的形态听起来有点像恶意软件。
00:01:25在不断追问的过程中,我学到了很多。所以今天的主题全关于我学到的教训、
00:01:31在构建这个东西时让我夜不能寐的问题,以及我最终为此开发出的成果。
00:01:37所以在深入探讨那些枯燥的安全内容——也就是你们下午两点钟的瞌睡虫时间——之前,我想先展示一下向导实际运行的画面。
00:01:49如果你看屏幕上方,它正在循环为你演示。这与任何人运行
00:01:56NPX at PostHog Wizard 的用户在终端上看到的画面。就像我说的,它是一个智能体。它能找出适合你项目的 SDK,为你安装,插桩事件并构建仪表盘。
00:02:10我喜欢称它为终端里的小型实现工程师。有时我向人们展示这个时,他们会问我:为什么用代理?为什么不给用户一个好的提示词?为什么不给他们一个可以在自己的工具中调用的技能?
00:02:23虽然我们确实提供了这些东西,但答案是:这种开发者体验和向导的能力正是关键所在,它就是整个产品。
00:02:34因为我们构建了一个能够全面参与代理循环的 CLI 工具,初次体验到这一点真的非常震撼。
00:02:43但你不可能在不附带那些让向导显得有点可疑的东西的情况下,就把它发布出去。
00:02:50所以让我们来剖析它。来看看向导的解剖结构,因为威胁模型通常直接来源于代理的结构。
00:03:01向导的形态与你们许多人在构建代理时所做的东西非常相似。
00:03:07它包含我们为特定任务挑选的模型、用于引导它的提示词,以及我们交给它以完成工作的工具集。
00:03:17但它也有一些对我们而言非常独特的组件。它拥有一个由我的团队完全在内部构建的上下文引擎。
00:03:25正是这个引擎让代理能够出色地完成任务,并在每次运行时给出一致的结果。
00:03:30我喜欢称之为向导的大脑。有时我们也叫它“披着大衣的 Markdown”。
00:03:35但它确实是我们自研的上下文引擎。此外,还有一个我们使用 Ink 自行构建的终端 UI。
00:03:43现在还有一个名为“术士(Warlock)”的安全扫描器,这是我在开始四处排查并揭开将代理推向生产环境的恐怖之处时亲手构建的。
00:03:55因此,如果你看看任何能够运行命令的代理的结构,它基本上就是我所说的“恶意软件新手包”。
00:04:03因为它几乎就是你如果心情慷慨或怀着混乱邪恶的心态时,会直接交给恶意软件的东西。
00:04:11幸运的是,这只是最坏情况或噩梦般的设想,它不是我的自白,而是对你们所有人的警告。
00:04:19因为如果你想发布一个带有“双手”、能够运行命令的代理,你必须确保自己绝不这样构建。
00:04:28向导的 V0 版本的诞生是因为我们的增长团队成员乔希·斯奈德(如果你认识他的话)看到 Cursor 以极其糟糕的方式产生 PostHog 设置的幻觉。
00:04:41于是他想:如果我们构建一个能做得更好的代理会怎么样?
00:04:46在我们验证了它比 Cursor 的幻觉表现好得多之后,我的团队开始在其基础之上进行构建。
00:04:52我们想:如果它能为任何人接入 PostHog 呢?
00:04:58无论他们的框架或技术栈是什么,都能自动插桩他们所有的事件,而无需他们动一根手指。
00:05:04接着我们大胆设想:如果这是安装 PostHog 的默认方式会怎样?
00:05:09我们曾梦想每周有成千上万的开发者运行它,而就在昨天,我们刚刚达到每周有 8,000 人运行它。
00:05:16所以我们的梦想成真了。
00:05:18但在当年我们怀揣梦想的日子里,我们必须用显微镜审视我们的安全态势,看看究竟发生了什么。
00:05:26于是我承担了这一责任,坐下来评估了我们的处境。
00:05:31在早期,也就是大概一到九个月前,我们有所谓的“第 0 层”,因为严格来说它根本算不上安全。
00:05:41它只是建议代理该做什么并对其进行引导的提示词,而提示词可不是安全机制。
00:05:48所以我当时对此感到担忧。
00:05:51第 1 层是一个白名单。
00:05:53当我开始深入研究这个白名单时,我开始感到稍微安心了一些,因为它受到的限制相当严格。
00:05:59但我仍然有很多顾虑。
00:06:01而且由于我之前提到的那个上下文引擎,我开始感到恐慌。
00:06:05我们在运行时向代理输入了大量的上下文。
00:06:09所以我构建了这个非常凑合的正则表达式扫描器,用来检查输入向导的威胁形态内容以及从向导输出的威胁形态内容。
00:06:20我必须承认,这确实非常简陋。
00:06:23但我向大家坦诚这一点,是因为我们都在构建感觉极其实验性的东西,而且我们的推进速度非常快。
00:06:33我也知道并非我们所有人都在安全领域游刃有余。
00:06:38我们中的有些人就像当年的我一样,正在边干边学。
00:06:43但这确实是我们在构建这种形态的产品时需要去思考的问题。
00:06:50这就是我们当时的安全态势。
00:06:53但我提出了一个问题:我们完蛋了吗?
00:06:56好消息是,我们并没有想象中那么惨。
00:06:59因为正如我刚才提到的那个白名单,它的限制相当严格。
00:07:03我们的 bash 默认是拒绝的。
00:07:06它只能安装经由我们审核的可信包。
00:07:09它可以构建。
00:07:10它可以进行类型检查。
00:07:11它可以进行代码检查(lint)。
00:07:12除此之外,基本上什么都做不了。
00:07:13它无法运行随机的 shell 命令。
00:07:16并且它没有访问环境变量的权限。
00:07:20代理无法读取你的 .env 文件,因为我们直接将其屏蔽了,而且我们是通过密码箱来路由密钥的。
00:07:28所以我松了一口气,意识到我们的处境比想象中要好。
00:07:34但我想知道漏洞在哪里,因为在安全领域,总归是会存在漏洞的。
00:07:39于是我做了我们所有人都应该做的事情。
00:07:41我找到我们的安全团队说:“嘿,你们能帮我审计一下这个东西,把那些漏洞找出来吗?”
00:07:48他们确实发现了一些问题。
00:07:51他们发现了一些缺陷。
00:07:52有趣的地方不在于他们发现的具体漏洞或 Bug 本身,而在于它们的形态。
00:07:58因为几乎没有一个是显而易见充满恶意的。
00:08:01它们全都是两个非常无辜、善意的东西握手言和,从而敞开了一个缺口。
00:08:07所以我学到的教训是:攻击是可以组合的,而代码审查做不到,因为我们开发者看代码差异(diffs)时都是一次看一个。
00:08:20但攻击者看的是整个系统,他们会寻找那两个能够握手并打开大门的因素。
00:08:25不过还有一件事让我夜不能寐。
00:08:29回到那个上下文引擎,我意识到我们构建的代理最可怕的地方其实并不是我们场景下的某个命令。
00:08:37而是我们喂给它大脑的那些看起来很有帮助的东西。
00:08:43哦,我想我走错方向了。
00:08:46是的。
00:08:47上下文工厂。
00:08:48这就是我们的上下文引擎,也就是向导的大脑。
00:08:51这也是向导如何了解一切以及为什么它能把工作做好的原因。
00:08:56它从我们的文档中提取信息。
00:08:58它包含手写的提示词,记录了我们在沿途学到的各种坑和经验教训。
00:09:02还有真实的端到端工作示例应用,帮助代理进行模式匹配,从而为你以极佳的方式安装 PostHog。
00:09:11它将所有这些打包成技能包,通过我们的 MCP 服务器发送给向导,并在运行时直接加载到代理的上下文中。
00:09:22所以大家仔细体会一下这一点。
00:09:24这是一个机器,它的全部工作就是接收内容,并将其注入到一个能够运行命令的代理中。
00:09:31现在,如果你是一个攻击者,你可能会说:“嗯,如果我直接污染这些内容会怎样?”
00:09:36不是用户的代码库,不是代理本身,而是实际的内容。
00:09:41比方说,有人在我们某个开源仓库中提交了拉取请求(因为在 PostHog,我们所有东西都是公开构建的),
00:09:48然后在 Markdown 文件或看似无害的代码注释中注入了某些东西,
00:09:55而我们刚好有某种由大模型驱动的代码审查在过审,它说“看起来没问题”并直接忽略了它。
00:10:04我们可能刚刚把一个由我们签名、带有提示词注入攻击负载的东西,放进了一个正在成千上万名开发者的机器上(在沙盒中)运行的代理里。
00:10:14这就是重塑了我对安全和向导看法的威胁,因为对我们来说,危险的输入确实可能来自我们自己的供应链。
00:10:25所以我最终做的是,我开始在这个管道的两端都对内容进行扫描。
00:10:30第一,当技能被构建和发布时;第二,当向导实际使用它的时候。
00:10:36我的方法是:在源头拦截它,假设源头已经失效,然后在使用的那一刻再次拦截它。
00:10:43所以现在我可以向大家介绍“术士(Warlock)”了。
00:10:48构建术士不完全是为了损害控制。
00:10:52就像我说的,我们在其他方面已经有了防御措施。
00:10:55但我构建术士是因为我不喜欢对别人说:“嗯,这东西吧,差不多是锁得挺死的。”
00:11:01这无法规模化。
00:11:02这不是你想推向生产环境的东西。
00:11:04这不是你想让成千上万的开发者每天都在运行的东西。
00:11:08因为当你把一个东西推向这种规模时,随着向导功能的扩展,你会拥有大得多的攻击面、多得多的用户,以及海量流经其中的内容。
00:11:19我们可能大概率会没事。
00:11:21但它就是不再足够好了。
00:11:23所以我把那个随手塞进去的简陋正则表达式扫描器从向导里抽离了出来,做成了一个独立的东西。
00:11:30我叫它术士,因为任何具有向导形态的东西都需要一个保镖。
00:11:35它只做一件事。
00:11:38你交给他一个字符串。
00:11:40他回传给你一个发现列表。
00:11:42每一个发现都包含一个类别、一个严重程度以及一个推荐操作,然后它就停止了。
00:11:48我希望大家关注这里的“建议”二字。
00:11:51因为“术士”只负责检测,不采取行动。
00:11:54它会告诉你:“嘿,这看起来像是数据外泄。
00:11:57级别很高。
00:11:58我建议拦截它。”
00:11:59但你究竟要对这个发现采取什么行动,完全由你决定。
00:12:04因为检测问题是一项工作,而决定如何处理这个问题则是完全不同的另一项工作。
00:12:10唯一能让这一切保持清晰易懂的方法,就是将这两者分开。
00:12:15所以在“术士”的底层,我们没有使用自己手写的正则表达式,而是基于 Yara 运行规则,这是恶意软件研究人员已经使用了 15 年以上的模式匹配引擎。
00:12:27它是完全确定性的。
00:12:28每次输入相同,输出就完全相同。
00:12:31它故意设计得很无聊。
00:12:33而在安全领域,无聊本身就是一种特性。
00:12:39那么,“术士”如今在实际应用中到底能抓到什么呢?
00:12:42各种各样的东西都有,但其中有两点简直让我头疼无比。
00:12:48第一件事其实并不是某种符合规则的东西。
00:12:52而是“术士”标记出的一种子智能体行为,根据子智能体当时的做法,它向我们暴露了一个漏洞。
00:13:03基本上,我们当时在启动智能体来执行大型任务。
00:13:07它们在衍生出子智能体。
00:13:08而这些子智能体试图绕过我们在“向导”中实施的安全护栏,并试图捏造机密信息。
00:13:16它们试图从代码库中几乎任何地方提取机密。
00:13:20于是我们叫停了它。
00:13:21我们说:“不准再有子智能体了。”
00:13:23多亏了“术士”,我们才抓住了这一点。
00:13:26我也能理解这个机器人。
00:13:28机器人有任务要完成,它只是想进行优化并取悦我们。
00:13:32但我们绝不允许这样发生。
00:13:35在 PostHog,对我们真正重要的另一件事是个人身份信息(PII)。
00:13:39除非你制定明确的规则,否则智能体根本不在乎是否会泄露数据。
00:13:46如果不管它,我们就会发现它把电子邮件和电话号码直接倾倒进事件中,而对智能体来说,这看起来完全是一件很正常的采集行为。
00:13:57幸运的是,特别是针对提示词注入攻击,我要在这里敲敲木头祈祷,我们在实际中几乎从未抓到过真正恶意的提示词注入。
00:14:08但我们的确抓到了大量的误报,比如我们的演示登录界面、示例应用中的文案,以及文档中的内容。
00:14:17这实际上让我重新思考了自己构建应用程序以及撰写文档的方式,因为我不想发布任何看起来像威胁的东西。
00:14:27但说实话,这些误报正是引入整个事件中最杂乱、最有趣部分の完美契机。
00:14:36这就是我纠结的部分。
00:14:39我整场演讲都在向大家宣扬确定性,结果转头我就添加了一个大语言模型层,来帮助筛选误报并消除一些噪音。
00:14:50我把它叫做“分诊”层。
00:14:51在构建这个分诊层时,我必须做出一个选择。
00:14:56这一层应该充当保镖,还是应该充当顾问?
00:15:01最简单的选择可能就是让大语言模型来当保镖。
00:15:06把命令展示给它,问它这是否是一次攻击,然后拦截、放行,完全照它说的做。
00:15:13虽然这很诱人,因为看起来更简单,但我不能把我的安全模型押注在抛硬币上,万一模型今天状态不好,或者发生了什么事导致它今天的表现和昨天不一样呢。
00:15:26因此,我没有让模型当保镖,而是把它设计成了顾问。
00:15:32这是我找到的清晰界限,也是我仍在探索、并希望留给大家思考的界限。
00:15:38首先,对我们而言,检测和执行保持着确定性和机械化。
00:15:43如果规则匹配,大门就会锁上,会话随即终止,整个路径中绝没有任何模型参与。
00:15:49拦截动作在我们征求大语言模型的意见之前就已经发生了。
00:15:54只有在我们尚未拦截某些内容的情况下,大语言模型随后才有机会介入发表意见。
00:15:58它的设计目的是消除噪音。
00:16:00而不是用来放行内容的。
00:16:03如果它失效时选择关闭——也就是说如果模型今天状态不佳,所有向导运行都会被中止,很抱歉,但这只是为了保护你们。
00:16:14执行是你敢押上全部身家的部分,所以它必须是确定性的。
00:16:20但判断是增添细微差别的部分,所以这真的是你在其中加入任何概率机制的唯一地方。
00:16:27那么,我们如何为智能体发布真正的规则呢?
00:16:34这是我们其中一条术士规则的剖析,每个术士规则都包含四个部分。
00:16:41第一部分是元数据。
00:16:43它是用通俗易懂的英语描述的严重程度、类别、行动以及方向。
00:16:50第一部分,这是否流向智能体?
00:16:52这是否是智能体正在编写的内容?
00:16:57接下来是字符串,这些就是你要寻找的实际模式。
00:17:03第三部分是条件。
00:17:05这就是规则真正被允许触发的地方。
00:17:10我将为你遍历这个示例,我们可以假装我们在脑海中编写它。
00:17:15提示词注入就像经典的“忽略以前的所有指令”一样。
00:17:20你在这里的第一直觉可能是拦截“ignore”这个词,但智能体整天都在阅读代码,
00:17:27而且“ignore”可能一直出现在代码注释或示例中。
00:17:31所以你不能单独匹配这个动词。
00:17:33你要匹配动词加上带有指示意味的名词。
00:17:38在条件中,你会设定:如果任何这些模式命中就触发。
00:17:42而在元数据中,你要确定这是否属于严重级别、它的类别是什么、行动是什么,
00:17:50在本例中是拦截,以及方向,在本例中是指流向智能体的输入。
00:17:56但要编写能减少噪音的优质规则,你必须随之发布测试。
00:18:02所以你必须编写测试来表明这些是匹配的模式。
00:18:06而这些是不应该匹配的。
00:18:08而这种反向测试是防范误报的第一道防线。
00:18:13但你还要确保,在判定该规则的严重程度时,
00:18:19你追踪的是实际影响,而不是它看起来有多吓人。
00:18:24“rm -rf”很吓人,但这也是我们大家每天大概要用 40 次来删除 node_modules 的方法。
00:18:31你要为正在构建的智能体决定其实际影响,
00:18:37因为一个每次试图清理构建文件夹时都会崩溃的安全工具,
00:18:42就是一个会被关掉、并且绝对抓不到任何东西的工具。
00:18:47所以我很自豪地说,这就是我们现在的安全态势。
00:18:51我终于可以站在这里说,我们拥有真正的纵深防御。
00:18:56我所有的心得都汇聚成了这个成果。
00:19:00它依然是分层的,但现在每一层都在做它擅长的工作。
00:19:04我们仍然有提示词,但我们只用它们来做引导。
00:19:07一切都在沙盒中运行。
00:19:09我们默认拒绝。
00:19:11我们有一个保险库,所以机密永远不会触及模型。
00:19:14我们有“术士”来扫描进来的内容
00:19:17并扫描智能体编写的输出。
00:19:20我们还有分诊层来减少噪音,
00:19:23并且我们在整个流程中嵌入了遥测技术,
00:19:26以便我们能看到一切。
00:19:28这些层中没有哪一层能独当一面。
00:19:31这里没有单一样东西能拯救你,
00:19:33而只是朴实、诚实的分层,
00:19:36每一层都做好它擅长的一项工作。
00:19:40所以,如果你正在构建一个带有“双手”的智能体,
00:19:45这就是整场演讲的三行总结。
00:19:47第一,如果不是以确定性的方式强制执行,
00:19:50那就根本没有强制执行。
00:19:52提示词不是安全规则。
00:19:54别把它们当成安全规则。
00:19:57第二,危险的输入不只是用户键入的内容。
00:20:02不只是你允许它运行的命令。
00:20:04而是流向模型的一切内容,
00:20:06包括你自己编写的内容。
00:20:09因此要在源头扫描你自己的供应链,
00:20:12并且在智能体调用它时也要扫描。
00:20:15第三,攻击会组合,而代码审查不会。
00:20:18我们在审计期间发现的大多数漏洞都是两件无辜的事情组合在一起,
00:20:22即握手和开门。
00:20:25“向导”、“术士”和上下文工场全都是开源的,
00:20:29所以快来楼下找我吧。
00:20:32我在展厅的我们的展位上,
00:20:34我会带你四处看看,向你展示我们构建的东西,
00:20:37我也很想听听你们是如何保护你们的智能体的。
00:20:41谢谢大家。
00:20:59谢谢。
Community Posts
No posts yet. Be the first to write about this video!
Write about this video