密钥不在源码里,它在发布物里
「几百万个 API Key 泄露在移动应用里」这类标题容易让人麻木,真实情况没那么戏剧化,但更普遍:硬编码的密钥每天都在被打包上线,而且往往就在明面上。
API Key 就是调用某个服务时出示的身份凭证,拿到它等于拿到那个账号在该权限范围内的一切能力。这篇文章的作者做了多年相关工具,他给的第一条纠正就是:别只盯源码仓库,密钥真正藏身的地方是发布产物。
具体有那么几类。移动端 APK / IPA 里,字符串被编译进代码、资源文件和 native 库,用现成工具就能提取;前端 JS bundle 是直接发给浏览器的,View Source 或者翻 Network 面板就能看到 API Key、Firebase 配置和第三方 SDK 的凭证;Electron 应用打包成 ASAR 归档(一种把整个源码目录塞进单个文件的打包格式),`npx asar extract app.asar out` 就能展开出完整源码树,Tauri 和 Wails 的二进制同理,资源是内嵌的。还有一些配置文件是「设计如此」地随应用发布的——`google-services.json`、`GoogleService-Info.plist`、`awsconfiguration.json`,它们本来就带密钥。
最后一类是新手最常忽略的:构建产物。CI/CD 日志、Docker 镜像、包仓库,都可能把构建期注入的密钥漏出去。
扫描交给工具,人只做判断
作者明确说:别手工 grep。先用专门的扫描器跑一遍,比如 trufflehog,它支持扫本地文件系统,也能直接扫 GitHub 仓库。这类工具的关键能力是熵值检测(判断一段字符串是不是随机到「像密钥」的程度)加上已知格式匹配(AWS、Stripe、GitHub、Slack 等各家密钥有固定前缀和长度)。两者结合,才能抓住正则匹配漏掉的东西。
扫出一堆候选之后,先验证再上报,这一步能省掉大量白工。看三件事:格式对不对得上服务商文档里的规则;这个 key 到底能不能用——大多服务商有轻量的校验接口,比如 AWS 用 `aws sts get-caller-identity --access-key $KEY --secret-key $SECRET`,Stripe 用 `curl -u $KEY: https://api.stripe.com/v1/account`;以及它的范围是什么——是可公开的 publishable key,还是 secret key。Firebase 配置对象里有些字段本来就是给公开用的,那不算秘密。
泄露了不等于事故,先算影响
不是每个暴露的 key 都是危机。接下来要问四个问题:这个 key 有什么权限,只读分析数据,还是能整库写入?能不能轮换(大多数能,少数老集成不行)?有没有被滥用的痕迹(能拿到服务商日志就看)?以及,应用离了它还能不能跑——有些 key 只服务可选功能。
报告的目的是修,不是涨粉
找对渠道:域名下的 `/.well-known/security.txt`(一个约定俗成的文件,里面写安全联系方式)、平台方项目(Apple、Google、微软和主流 SaaS 都有专门的 security@ 邮箱或 HackerOne、Bugcrowd 之类的漏洞赏金平台)、或者 `security@<域名>`,再不行就用普通联系表单,主题里写清 SECURITY。
报告怎么写,作者给了一份可直接套用的结构:标题用「Potential exposed API key in [应用名] v[版本] - [密钥类型]」;正文说明在哪发现的(精确到 `res/values/strings.xml`、生产环境的 `/main.abc123.js`、Electron 的 asar 归档这种路径)、密钥前缀(打码,只露前几位)、应用版本与平台、这个 key 是有效还是无效、依据服务商文档它能拿到什么权限;然后给可复现步骤:从哪下载、怎么提取、用哪条命令验证;最后一句写「乐意配合轮换的时间安排」。
该做的和不该做的都很具体。该做:初次报告里把完整密钥打码、给出精确位置和复现步骤、影响陈述只讲事实不夸大、主动提出配合轮换。不该做:在披露前公开发现、索要报酬(除非走正式赏金流程)、超出基本验证去测试(别枚举资源、别写数据)、把 key 转给任何人、以及夺命连环催——给对方 7 到 14 天初次响应的时间。
真正该修的,是别让它被打包进去
找 key 是补救,治本是让它根本不上线。作者给的方案按层次排:
**别把密钥放进应用包。**走后端代理:应用 → 你自己的 API → 第三方 API,密钥留在后端,应用只拿一个短期令牌。移动端把敏感数据放进 Android Keystore / iOS Keychain,抬高风险门槛;把密钥扫描接进发布流水线,扫到就让构建失败;构建期环境变量要分清,公开前缀(比如前端框架里那些 `PUBLIC_` 之类)意味着「这个会发到浏览器」,那里只能放可公开的 key;敏感配置改成应用初始化时带鉴权从后端拉;再加一层 CSP(内容安全策略,限制页面能从哪些来源加载脚本),降低密钥被偷后的影响面。
**桌面端(Electron / Tauri / Wails)同理**:别把密钥打进 asar 或 resources,照样走代理,用操作系统的凭据管理器存东西,并且签名 + 公证,让篡改变难。
**所有平台共同的**:定期轮换,能自动化的就自动化(AWS IAM、Stripe、GitHub 都支持);坚持最小权限,一个只能读分析的 key 再怎么样也比全写权限的安全;盯住服务商提供的最后使用时间和调用量,出现异常就去查。
有一种 key 本来就不是漏洞
这段值得单独记住,因为它是误报的主要来源。设计上就该公开的包括:Stripe 的 publishable key、Firebase 配置对象里的那些字段、带 referrer 限制的 Google Maps API key、Algolia 的 search-only key。这些不是漏洞,别去报。把各家的密钥分类搞清楚,能省所有人的时间。
长期做这件事的人还会顺手经营关系:报告清晰可复现、尊重协同时间线、不摆姿态、修完之后还跟进确认上线——这样的报告人会得到更快的响应,有时还能拿到直接对接人和内测机会。
内容与图片版权归原作者所有 · 原文: https://dev.to/cekuu35/finding-exposed-api-keys-in-shipped-apps-without-being-a-jerk-1hj9