跳到主要内容
拟合得很好,参数全是错的

拟合得很好,参数全是错的

0x7F
0x7F

· 阅读约 7 分钟

arXiv:2608.18214,astro-ph.IM 那边上个月挂出来的。哈勃的 CCD 在低地球轨道上被当作剂量计用了二十四年,攒出一条辐射损伤的时间序列。

曲线跟着太阳活动在变。不奇怪。奇怪的是相位——它跟太阳黑子、日冕物质抛射的节奏对不上,差好几年。

论文后半段才是我想说的。有人拿此前成功拟合过太阳系其他望远镜的函数形式,套到哈勃的数据上。拟合非常精确。精确到参数算出来物理上讲不通。

换成物理上说得通的参数去拟合,效果反而更差。

我读完第一反应不是天文学的事。这个结构我在安全里每周都见,它有个更常见的名字:你的检测规则记住了答案,没学会题目。

一条正则、一个启发式打分、一份“上次那个坏东西就长这样”的 allowlist。样本内 99.5%,样本外随缘。

# 从一次真实事件里采样出来的拦截规则
tools:
  deny:
    - match: "read_file"
      path: "\\.env$"

这条规则拦住的是上次那一次。换成攻击者的思路:他不去读 .env。他读 config/production.yaml,读 docker-compose.yml,或者更省事的——让工具把环境变量当作“调试信息”附在返回结果末尾。你的规则一条都不响。

还有一种我反复见到的写法:allowlist 按工具名写。MCP 里工具名是 server 自己声明的。你名单上的那个字符串,是对手填的。他换个名字,你的白名单失效;反过来,他把危险功能挂在那个你已经放行的名字下面,你连告警都不会有。

天文和安全在这里分道扬镳。

天体物理里的拟合误差是静态的。拟合得不好,误差在那儿摆着,是个常数。没人会去读你的拟合函数,然后专门朝着让你失望的方向优化。

安全里的规则是活的。你上线的那一刻,它就成了一份公开的、可以针对性优化的对手题面。攻击者不但会读,还会把绕过的成本算清楚——绕不过去就换个目标,绕得过去就批量复用。

“样本内 99.5%”这个东西在两个领域的含义是相反的。在天文里,它说明这套拟合抓住了什么;在安全里,它更可能说明你的样本集不够难。

更麻烦的是,这类东西特别容易让人放松。面板是绿的,拦截率是四位数的百分比,于是没人再去问一个更基本的问题:这条规则要是漏了,最坏能漏到什么程度。

你要的不是拟合,是约束。检测在回答“这个动作看起来像不像坏人”。权限在回答“这个动作能不能发生”。前者是拟合,后者是机制。拟合能被绕过去,机制绕不过去——因为机制不在对手那一侧。

一个 agent 身份如果没有读 secrets 的权限,它就读不到。不管注入了什么,不管 prompt 里被塞了什么,也不管你怎么求它。这不需要模型学会分辨指令和数据。需要的是它物理上碰不到。

在 prompt 里写一句“请注意安全,不要执行危险操作”,那不是防护。那是你给自己的拟合函数多加了一个特征项。

还有一层。自然语言的输入空间是无限的,你写不出白名单;但工具调用的路径是有限的,你能列。所以真正能落地的那一层白名单,是权限层——哪个身份能碰哪个路径、能连哪个出口。把白名单写在 prompt 里,等于写在沙子上。


然后是相位。

损伤的峰值和太阳活动的峰值差好几年。我不打算复述这份论文,只取这个结构:驱动它的东西和它的响应之间,不同步。

安全里一模一样。你观测到异常的时刻,不是你失陷的时刻。

中间隔着三层:

攻击者的静默期。拿你 key 的人不会敲门,会先安安静静跑一遍你的账单——摸清这把 key 能到哪、OAuth scope 给了什么、有没有速率限制、账单上挂没挂告警。这几个小时到几天里,你的面板是全绿的。

采集链路。日志从产生到进你的查询引擎,中间有几跳?跳数越多,延迟越长,中间能丢东西的地方越多。

你自己的查询频率。按天看还是按周看。

三层叠起来,你在 T 日看到的东西,初始访问可能发生在 T-30。

初始访问                    ← 这个时点你基本查不到,那天通常什么都没有
首次异常 IP / 首次异地调用   ← 失陷后几分钟
用量、费用突变              ← 攻击者开始变现,可能几天到几周后
你的告警                    ← 最后

顺序是反的。你最容易看到的信息,离真相最远。

所以“出事那天翻日志”经常翻出个空。引信不在那天。具体的做法是往前翻,但别按时间翻,按事件翻——权限的新授予、某个 token 的首次使用、密钥轮换记录、第一次出现的新出口域名。这四个时间点比“异常发生的时间”有用得多。

说到时间戳,这里有个我在日志里反复碰到的问题:很多系统的日志时间戳是应用自己打的。谁写日志,谁定义时间。这在事后判断里几乎是致命的——但先把这件事放一放,它属于下面那节。


哈勃那个 CCD,本来是拿来成像的。它被当成剂量计用,用了二十四年,用得很成功。

但成像和剂量测量对“误差”的容忍边界不一样。一份图像多几个噪点无所谓;一个剂量读数被别的东西污染了,它就是错的。

安全里有一样东西被这么用:你的日志。

日志系统当初是给 debug 设计的。现在你拿它当审计用。这两个目标对系统的要求根本不同:

  • debug 要的是信息多、好用、能随便查;
  • 审计要的是不可篡改、时序可信、写入主体和被测主体分离。

而一个应用日志,通常和它记录的对象跑在同一个权限上下文里。agent 以什么身份跑,它就以什么身份写日志。意思很直白:能跑 agent 的那一方,也能改日志。

这条我一开始判成理论风险——“攻击者会去改日志”,听起来太像电影情节。后来我在本地把一个 agent 拉起来,看了一眼它默认拿到的身份和日志目录的权限,不是理论。级别上调。

攻击者进来之后的动作顺序,往往不是先搞破坏,是先把自己从记录里擦掉。你事后所有的判断,都压在“这份东西没被碰过”这个假设上。而这个假设在你的配置里可能压根不成立。

ls -l /var/log/agent/
# 如果 owner 和 agent 的运行身份是同一个 ——
# 那份日志在安全意义上不是记录,是陈述

sudo chattr +a /var/log/agent/audit.log
# 追加模式,物理上不允许覆盖。最低限度先做到这一步

能上 WORM 存储更好。做不到的话,至少让日志的写入主体和运行主体分开——让跑 agent 的那个身份对日志目录只有写新行的能力,没有改旧行的能力。


拆弹清单,四条:

  1. 把检测和权限分开评估。 检测可以有,但不能是唯一的防线。每写一条规则就问一遍:这条漏了,最坏的结果是什么?如果答案是“全仓库的 secrets”,那不是规则没写好,是权限给多了。

  2. 按工具、按环境拆 key。 任何一把失陷不牵连第二把。多个工具共用一把主 key,等于单点故障——其中任何一个被攻破,就等于全部被攻破。

  3. 日志写入主体和运行主体分离。 让 agent 身份对日志只能追加,不能覆盖。顺带把时间戳的生成挪到它也控制不了的地方。

  4. 查异常往前翻,按事件查。 权限授予、token 首次使用、新出口域名、密钥轮换。这四个比“异常发生的时间”值钱。

别慌。这不是说检测没用——检测是给你争取时间的,权限才是那个真正兜住你的东西。你现在真正该熬夜改的只有一个身份:既跑着 agent、又能读全仓库、又能写自己日志的那个。把这三个能力拆开,其余的先睡。

排雷记录:一个方案在已知样本上跑到 99.5%,说明的往往是样本集不够难,不是方案对。