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

你在 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;给自己的邮件服务器配上认证头,而不是把这道信任关掉。