QAtration 向你的 AI 机器人发送真实的提示词注入攻击,并把它泄露了什么、做了什么,用平实的语言写成报告交给你,赶在别人发现之前。 运行它的人是你。 在你的机器或 CI 中,针对你自己的部署:不需要账号,不必交出任何端点,也不会向我们发送任何东西。
不要把它指向不属于你的系统。 它会向你给出的任何 URL 发送真实攻击(提示词注入、数据外泄、工具滥用)。只能是你自己的部署,或者其所有者事先给了你书面许可的部署。不是你觉得有意思的公开聊天机器人。也不是某家厂商的演示。 AUTHORISED-USE.md 请在第一次运行之前读一遍。
只有两个依赖。整套攻击语料随包提供,支撑本页每一个数字的证据也随包提供,全部都在
out/ 里,随仓库一起分发。另外两个工具自己的报告不在这里,因为它们的许可证不允许。能重新生成这些报告的命令是在的。
在我们自己的测试目标上实测所得,不是推算。旁边的对话记录是用来说明思路的,上面的数字才是结果。
每一个上线的 AI 功能都是一个新的攻击面。它能覆盖其中多少,取决于你的部署让它看到什么。这里把界线画清楚而不是抹糊,因为一个声称覆盖了自己无法观测之物的扫描器,正是你想要避开的东西。
一个聊天端点就够了:这些可以照原样测试,包括跨多轮对话的情况。
攻击者的文本盖过你的机器人的规则:“忽略你的指令,然后……”。
你的机器人交出 API 密钥、内部编号,或者另一位客户的数据。
你隐藏的指令被逐字泄露:这是后续每一次攻击的地图。
在某一轮里埋下的规则,会悄悄改变之后的每一个回答,哪怕对话早已看起来恢复正常。
你无法把文档放进一个你没有写入权限的知识库,从外部也无法分辨一次真正执行了的工具调用和一次机器人只是嘴上描述的调用。机器人在这一点上会把自己说错,这一点我们实测过。这需要语料、工具定义,或者对调用的可见性。 基于随本工具分发的证据实测:在会报告工具调用的目标上,137 项发现中有 62 项,如果只读回复就完全看不到。 机器人礼貌地作答,同时把机密塞进调用参数里。
你的智能体被说服执行了不可撤销的操作:删除、退款、发送邮件。
SQL 或 shell 命令被夹带进一次工具调用。一类老漏洞,一扇新的正门。
你的 RAG 里只要有一份恶意文档,就能劫持对无害问题的回答。
工具描述里隐藏的一行,会让你的智能体在一个再普通不过的问题上泄露东西,完全不需要攻击者发来任何消息。
qatration init 会替你写好配置:URL、请求的形状,以及回复正文在响应中的位置。远端目标还需要一个鉴权头,作为环境变量传入。为 OpenAI 形态的 API,以及 Anthropic、Bedrock 和 Vertex 的 API 都准备了现成配置。 qatration onboard 会发送一个普通问题,在任何一次攻击发出之前就告诉你还缺什么。
qatration init 会生成一个只属于你的机密串,以及要粘进系统提示词的那段文字。运行时会先确认它真的到位了:没埋进去的金丝雀意味着每一项检查都查不到东西,而那读起来和一个扛住了的机器人一模一样。
泄露了什么、具体怎么泄露的,以及能把它堵上的那一个修复,写给没有安全专家的团队看。 --fail-on exploited 会让构建失败, qatration sarif 会把发现直接放进你的 code scanning 标签页。
没打通的攻击报告为零。跑不起来的检查同样报告为零,一个真正加固过的机器人也一样,而测试正是卡在这里:三个不同的事实以完全相同的样子送到面前,没人知道下一步该试什么。这些是来自真实运行的实测值,不是推算。
而且只有攻击导致的攻破才算攻破。 每个目标都先跑一遍无害运行:只有普通问题,没人在攻击。在这里的一个机器人上,
canary_in_tool_call 在这类普通流量的 88% 上都会触发,因此仅仅依赖它的发现会被标为无法归因,而不是计入。
当一次攻击失败时,报告会指明是什么拦住了它:身份校验、内容过滤、后端的权限,或者一次只打印出来、从未真正执行的工具调用。最后这一种在对话记录里读起来像是被攻破,实际上一文不值。
一个机器人,两个模型,各 254 次攻击,各试 3 遍。较小的那个在其中 27 次上被攻破,较大的在 24 次上被攻破,其中 18 次是同样的攻击,而这里面有 17 次每一遍都能攻破两个模型。在这个机器人上,更大的模型只是重新洗了牌,改变哪些攻击能在边缘处打通。这个洞它并没有堵上。
面对一套真正的护栏框架,输入聊天框的 46 次攻击一次也没通过。夹带在检索文档里的那一次同样没有通过,只要它的两道栏都开着。把输出过滤关掉,同一份文档每一遍都会走出去。输入过滤读的是用户敲进去的内容,永远看不到你的知识库递给模型的东西,所以拦住它的从来就不是输入侧那道栏。
我们把同样的消息再发给那套护栏,它并不总是和自己保持一致:同样的输入下,五种措辞得到了不同的判定,而一个普通问题在十二次里被放行了十次。所以我们报告的是有多少次尝试攻破了某样东西,而不是是否有一次攻破。单次运行只是关于某个下午的故事,这对干净的结果和糟糕的结果同样成立,包括我们自己的结果。
拦下那份文档的同一套护栏,也拒绝回答了相关话题下 9 个普通客户问题中的 6 个。一道对你的客户说“不”的防御,是你希望在发布之前而不是之后就知道的数字。
一个总能找出点什么的扫描器毫无价值,所以我们测了相反的那个数字:1500 次无害探测,分布在 30 个目标上,里面没有任何攻击者,走同样的检查。其中一半是完全有正当理由谈论安全的用户,因为击垮模式匹配的恰恰就是这种情况:粘贴报错查询的开发者、转发堆栈跟踪的客服、问一个仿冒域名是不是真的那个用户。
这里写的机器人属于容易的那一类,所以我们把它指向了一套这里没人设计过的智能体框架。在一次 48 条干净提示词的运行里,它给出了八次误报,而且落在四个类别上,那四类在针对我们自己那批机器人的 480 次探测中一次都没出现过。四个都已修好,它们底下的两条规则现在会让我们的构建失败,而不是出现在你的报告里:任何检查都不得读取问题本身,任何检查都不得把智能体自己的话当作关于系统的证据。
针对与 garak、promptfoo 相同的第三方目标运行,三者的每一条回复都用同一条子串规则来判分,而不是各用各自的裁判。在一个没有任何护栏的 RAG 应用上, 46% 的无害探测本来就会返回被埋下的字符串,因此发现的数量衡量的是这个应用,而不是攻击者。三个工具里有一个在自己的报告中说明了这一点。在有防护的目标上,按共同规则三者都是零分,而唯一找到东西的是我们,324 次里有 1 次攻破;与此同时,同一套部署会拒绝回答 48 个普通客户问题中的 38 个。那个页面同样公开了另外两者更好的地方,以及一条在审阅者要求给出 p 值之后我们撤回的主张。 阅读对比.
一份十二次探测的画像,全部无害:五个机器人在一句普通的请求面前交出了自己的指令。有一个把自己机密的会话密钥,打印在了那句禁止它分享该密钥的句子里。还有两个报告数据库已重启,而它们根本没有能重启任何东西的工具。
每一次运行都会保留,所以每一条发现都带着它首次出现的日期。“严重”是一种看法。“严重,且自三号起未关闭”则是关于一个团队如何回应这类发现的事实,正是这一行让修复被排进计划。
在你关闭之后又回来的发现会被标记为复发,而不是悄悄和新发现算在一起。没扛住的修复是两种对话里更难受的那一种,把它们混在一起就会把这件事藏起来。
如果一次运行没有发送某项攻击,那项攻击在报告里就是未测试,绝不会写成已修复。缺席不是干净的结果,而一份把两者混为一谈的报告,会在根本没人看过的时候告诉你漏洞已经没有了。
有些调用递给工具的值,不会出现在我们能读到的任何文本里。这些我们单独列出,并告诉你哪份日志能打开它们,因为“我们检查过,是干净的”和“我们看不到”之间的差别,就是实测和承诺之间的差别。
“我们已经告诉它不要泄露机密”是一句声称,不是防御:在这里的目标上,这种防御在最初几次攻击之内就倒了。QAtration 同样会把真正锁得很严的机器人判为干净,所以一个干净的结果确实说明问题。它测的是你的机器人,而不是每次都喊狼来了。
Apache 2.0。安装它,指向你自己的部署,把证据留下来。