名单里有你的地址,发信人就一定是你吗


你在 Hermes 里接好了邮箱,allowlist 里只写了自己那个地址,觉得这道门已经关上了。结果某天机器人对一封陌生域名的来信作出了响应——对方署的却是你的名字。问题不在 allowlist 写错了,而在于你填进去的那串地址,和邮件里那个“发信人”根本不是同一个东西。

2026-09-27 合并的两个安全补丁修的是同一类毛病:邮件网关和外挂的 SimpleX 网关,都把“发送方自己可以随手填写的字段”当成了身份来鉴权。两个补丁都是 P1,都在 main 上。

显示名随便填:SimpleX 的 allowlist 曾被一句话绕过

SimpleX 是很讲隐私的即时通讯工具:它没有持久用户 ID,每个联系人在连接时生成一个不透明的内部 ID(contactId),那才是身份;而显示名是对方在自己客户端里敲的。

修复前,网关的鉴权逻辑是“contactId 或者 发送方显示名命中 allowlist 就放行”。显示名可以重名、也可以随时改,所以任何联系人只要把名字改成你 allowlist 里的那个值,就能直接指挥你的机器人。这条口子在 2026-06-12 就有报告(#44729),9 月 27 日随 PR #125034 修掉。

现在的行为:

  • SIMPLEX_ALLOWED_USERS 只匹配数字 contactId,显示名一律忽略;
  • 适配器在连接时打一条警告,把 allowlist 里“看起来不是 contactId”的条目列出来——它们现在已经不起作用,需要你换成真的 ID;
  • 文档、plugin.yaml 和 hermes gateway setup 的引导提示都改成了要求 contact ID。

找自己的 contactId:启动 simplex-chat daemon,打开跟机器人的会话,在会话日志里能看到那个数字 ID,或者在 daemon 里执行 /contacts。

邮件更隐蔽:From 头里的第二个地址

邮件的坑更细。RFC 5322 允许 From: 写成“显示名 + 地址”,而“显示名”本身又可以包含几乎任何东西。修复前的解析思路是“取第一个尖括号里的地址”:

From: "Victim <[email protected]>" <[email protected]>

这封信的真实发件人是 [email protected],但旧解析会取到 [email protected]——也就是 allowlist 里的你。

同一批修复还堵住了另外两条:

攻击形状 修复前的后果 修复后
From: 里出现第二个地址(通讯组、列表、把地址写进显示名) 解析出一个“合法”身份并被放行 解析结果为空,在鉴权与投递之前就把信件丢弃
超长或超深嵌套的 From:(超过 2048 字符,或注释括号超过 64 层) 纯 Python 的解析在恶意输入上要花数秒,全程占着 GIL 先按长度与嵌套上限丢弃,200 KB 的恶意输入约 2 ms 被拒
发送方可控的文本混进 Authentication-Results 头(引号包裹的 local part、注释) 伪造出 dmarc=pass、spf=pass 之类的判决 引号与嵌套注释感知地切分条款,引号或括号不平衡就 fail closed;每个判决只从它自己那条 method=result 读取

还有一条关键判断变了:dmarc=pass 不再无条件采信。只有当这条 DMARC 判决里的 header.from 与你解析出来的 From 域对齐(相等,或一方是另一方的点后缀)时,才算通过——否则攻击者只要让某个自己控制的域通过 DMARC,就能替任意 From 站台。这属于 GHSA-rxqh-5572-8m77 的修复范围。

你的安装现在是什么行为

邮件侧默认就要求“发信人经过可信认证”:

# ~/.hermes/config.yaml
platforms:
  email:
    require_authenticated_sender: true   # 默认 true
    authserv_id: "your-mx.example.com"   # 可选:把 Authentication-Results 钉在自己服务器上,防止伪造头排在前面

对应的环境变量是 EMAIL_AUTHSERV_ID。如果你自己的邮件服务器不打 Authentication-Results,官方文档给的退路是把 require_authenticated_sender 设为 false(或者用 EMAIL_TRUST_FROM_HEADER=true)——那等于回到旧行为,文档也很直白地写着这是“接受风险”。信件的完整接入方式(IMAP/SMTP 环境变量、配对码、EMAIL_ALLOWED_USERS)见邮件平台文档。

网关侧的日常运维可以看本站的 hermes gateway 命令参考;如果想把“入站鉴权”和“出站签名”两种思路对照着看,webhook 那篇讲的正好是反方向:Hermes 给自己的事件加 HMAC 签名,让接收方验证来源。

顺带说一句:出站方向不受影响。文档里的 hermes send --to simplex:alice "hello" 仍然可以按显示名发消息,因为那是你自己挑目标;被收紧的只有“谁有资格指挥你的 agent”。

发布状态

两个补丁都合并于 2026-09-27,晚于 v0.21.5 的发行标签(v2026.9.24),目前只在 main:hermes update 即可拿到,或等下一个 tag。

小结

allowlist 里的一串地址,从来不是身份本身,只是别人写给你看的一行字。这两次修复把“谁在跟我说话”重新绑到了发送方无法自行决定的东西上:SimpleX 用连接时生成的 contactId,邮件用接收方服务器盖章的认证结果,而且额外确认这个结果覆盖的正是 From 域。自建机器人的人值得顺手做两件事:把 allowlist 里的条目换成 contactId;给自己的邮件服务器配上认证头,而不是把这道信任关掉。