发生了什么
10 月 21 日,Railway 披露有未经授权的一方访问了存储客户环境变量的系统,并且其中一部分变量的解密内容可能已被攻击者获取。Railway 暂停了所有部署,通知上游服务商吊销密钥,并要求所有用户轮换环境变量,包括私有镜像仓库的凭据。
它的初步报告补充了更多细节:
- 攻击窗口从 10 月 20 日持续到 10 月 22 日凌晨(UTC),起点是构建基础设施中的一次提权尝试。
- 最多 1.85% 的工作区(约 10 万个)有部分变量可能已泄露。由于密钥的加密方式,Railway 目前还无法确切说明受影响项目中哪些机密被暴露。
- 攻击者专门搜寻 AI 服务商的 API 密钥和平台 API 密钥。Railway 将环境变量很多或 AI 密钥很多的工作区标记为高风险。
- Railway 表示无法退还使用被盗密钥产生的 AI 费用,你需要自己去找对应的服务商交涉。
据 TechCrunch 报道,Railway 认为这是一场针对各类平台的更大规模攻击活动的一部分,目的是攻陷 AI 推理服务商。同一周,AI 编程工具 OpenCode 也披露,黑客窃取了超过 1% 客户的 API 密钥和其他机密。
为什么开发者会成为目标
被盗的 AI 密钥能换来真金白银。攻击者会转卖访问权限,或者拿你的额度跑他们自己的任务,研究人员把这种模式称为 LLMjacking。托管平台是收割这些密钥的高效场所,因为成千上万个应用把 OpenAI、Anthropic、Stripe 和数据库的密钥都放在同一个地方。
Railway 的报告里有一个让人不太舒服的细节:这些变量是加密的,但平台必须能够解密它们,才能注入到你的构建和运行中的服务里。一旦攻击者拿到了合适的内部权限,静态加密就挡不住他们了。这并不是 Railway 独有的问题:任何运行你代码的托管商都能读取你的机密。
所以真正的问题不是“我的托管商安全吗?”,而是“如果我的托管商明天泄露了机密,后果有多严重,我能多快恢复?”
如何评估你的暴露程度
花 20 分钟做这件事,即使你已经对几个变量点过“轮换”。
1. 盘点每一个机密。列出每个项目和环境中的变量名,包括预发布和预览环境。如果你有 .env.example 文件,它是个不错的起点:
grep -oE '^[A-Z0-9_]+' .env.example对每一项,记下它能解锁什么:数据库、支付服务商、AI 服务商、邮件、存储桶,还是 GitHub 令牌。
2. 检查 AI 服务商的用量和账单。查找 10 月 20 日以来的用量激增、陌生的模型或异常时段的请求。滥用最有可能首先在这里露出端倪。
3. 检查你手上有的审计日志。Railway 特别建议查看 GitHub 审计日志和已安装应用的访问权限,以及 Docker Hub 等镜像仓库的日志。另外也看看支付服务商的 API 日志和数据库的连接日志。
4. 检查你的数据库是否能从互联网访问。Railway 的报告建议移除数据库的公网访问。如果泄露的连接字符串指向一个任何人都能连接的数据库,这次泄露就可以被立即利用:
nc -zv your-db-host.example 5432 # "succeeded" from your laptop = publicly reachable5. 检查你的前端已经暴露了什么。这类泄露事件提醒我们,要把真正的机密和本就设计为公开的值区分开。打包进 JavaScript 里的密钥从一开始就不是秘密。
如何在不影响生产环境的情况下轮换
人们在压力下常犯的错误是先吊销旧密钥,再手忙脚乱地部署新密钥,而这期间应用一直在报错。请改用以下顺序:
- 在服务商处创建新凭据(新的 API 密钥、新的数据库密码、新的 webhook 密钥)。暂时保留旧凭据。
- 在托管平台上更新变量,并重新部署,让运行中的应用用上新值。
- 确认应用在新值下正常工作。调用一个真正用到该服务的接口。
- 在服务商处吊销旧凭据。
- 确认旧凭据已失效,例如用它发起一次测试调用,检查是否失败。
按影响范围从大到小处理你的清单:支付密钥、数据库凭据、云和存储密钥、AI 服务商密钥、GitHub 和镜像仓库令牌,然后是其他所有东西。Railway 自己的指引重点关注 10 月 21 日之前设置的变量,其后续建议还包括重新连接 GitHub,以及重新创建 CLI 和 CI 凭据。
有几类机密需要格外小心:
- 会话签名或 JWT 密钥。更换后所有用户都会被登出。在发生泄露之后这通常可以接受,但如果可能,请提前告知用户。
- Webhook 签名密钥。接收端和发送端服务商要同时更新,否则你会在一段时间内拒收合法事件。
- 数据库密码。如果平台支持,创建一个拥有相同权限的新数据库用户,把应用切换过去,再删除旧用户。这比原地修改密码更干净。
让下一次泄露没那么痛
你无法阻止托管商被攻破,但你可以让泄露的机密价值更低。
严格限定密钥权限。服务商提供受限密钥时就用受限密钥。一个只能创建结账会话的 Stripe 受限密钥,对攻击者的用处远小于完整的私密密钥。
设置消费上限。为每个 AI 服务商设置月度预算和用量提醒。这样,一笔五位数的意外账单就会变成一封提醒邮件。
隔离各个环境。不要在预发布或预览部署中复用生产环境的密钥。每个环境都应有自己的凭据,这样一个环境泄露不会连累另一个。
让数据库保持私有。使用平台的私有网络;如果你用的是 Supabase,请确保已开启行级安全(Row Level Security),这样泄露的 anon 密钥也不会暴露你的表。
永远不要把机密放在可被访问的文件里。部署到 Web 根目录下的 .env 文件,不需要任何入侵就会泄露。
现在就把你的轮换手册写下来:第 1 步得到的清单、上面的顺序,以及每个密钥在哪里管理。下次轮换只需要一个小时,而不是一个周末。
快速检查清单
对你部署的每个应用都过一遍:
- 每个环境中的每个机密都已列出,并注明它能解锁什么。
- 每个机密都按照“创建、部署、验证、吊销”的顺序完成了轮换。
- 已查看 10 月 20 日以来 AI 服务商的用量和账单,并设置了消费上限。
- 已检查 GitHub、镜像仓库、支付和数据库日志。
- 数据库无法从公网访问。
- 生产环境和预发布环境使用不同的密钥。
- 前端代码中没有真正的机密,Web 可访问的文件里也没有。
要点总结
Railway 迅速披露并推动用户轮换,这是正确的做法。但这类事件在托管和开发者平台上一再发生,因为密钥就在那里。能从容应对的开发者,是那些清楚自己有哪些机密、并能在一个下午内把它们全部换掉的人。
在清理的同时,也值得从外部检查一下你的应用暴露了什么。AppSafe 会扫描你的线上网站,查找常见问题,例如 JavaScript 包中的 API 密钥、暴露的 .env 和 .git 文件,以及缺失的安全响应头,并给出通俗易懂的修复建议。我们为 AI 构建的应用准备的上线检查清单涵盖了其他基础事项。
轮换完所有密钥后,运行一次免费的 AppSafe 扫描,确保没有其他东西在泄露。
