Security Blog

查看源码,复制密钥,掌控一切

#53

October 2, 2026 · By Marketing team

← All posts

一名研究人员打开 ClickUp 的页面源码,在 JavaScript 中发现了一个硬编码的 API 密钥,并用它在一次请求中拉取了 959 个电子邮件地址和 3,165 个内部功能开关。该密钥没有权限范围、没有速率限制,也没有有效期。

一名安全研究人员访问了 clickup.com。打开页面源码。在 JavaScript 中发现了一个硬编码的 API 密钥。复制下来。发送了一个 GET 请求。

拿回了 959 个电子邮件地址和 3,165 个内部功能开关。来自 Home Depot、Fortinet、Autodesk、Tenable、Rakuten、Mayo Clinic 的员工。

一个字符串。一次请求。全部到手。

这是怎么发生的

有人需要前端调用某个 API。该 API 要求身份验证。于是他们把密钥放进了 JavaScript。发布上线,继续推进,下个迭代再说。

这不是什么复杂的攻击。没有漏洞利用,没有零日漏洞,没有社会工程。用的是 view-source: 和 curl。一个浏览器加一个终端。好奇心强的实习生入职第一天就能干出来的事。

该密钥没有权限范围——它可以访问该 API 暴露的一切。没有速率限制——一次请求就返回了全部内容。没有有效期——密钥一直有效,直到有人发现为止。没有第二因素——拥有这个字符串本身就是唯一的门槛。

从价值角度看

人们谈论数据泄露。我们来谈谈这些数据值多少钱。

959 个来自《财富》500 强企业的企业邮箱地址。这是一份威胁行为者愿意付费购买的鱼叉式钓鱼目标名单。姓名、职位,以及这些公司在使用 ClickUp 这一事实——正是这些社会工程背景信息让钓鱼得以奏效。

3,165 个内部功能开关。这是一份产品路线图。它告诉竞争对手 ClickUp 正在构建什么、测试什么、什么功能还在门禁之后。它告诉攻击者哪些功能只做了一半、很可能存在漏洞。

这不是一起隐私事件。这是一次商业情报泄露。

为什么这类事件屡禁不止

这是我们本月撰写的第四起源码中泄露凭证的事件。Bitwarden 的 CLI 中的凭证被收集,因为它们是明文文件。Vercel 的环境变量可以被解密,因为"敏感"标记并非默认开启。一名开发者丢失了 634 个 Chrome 密码,因为解密密钥就在同一块磁盘上。

模式始终相同:某个凭证以字符串形式存在——在文件里、在变量里、在页面源码里——然后有某个东西读取了它。那个"东西"会变。模式不会变。

JavaScript 中的 API 密钥是最恶劣的一种,因为根本不需要攻击。密钥是公开发布的。它被提供给每一位访问者。浏览器下载它、渲染它,并把它展示给任何右键点击的人。

本应如何改进

这个 API 调用根本不应该用客户端的静态密钥进行身份验证。可选方案:

  • 后端代理。 前端调用您自己的后端,由后端在服务端持有密钥并代理该 API 调用。密钥永远不会到达浏览器。
  • 会话级令牌。 前端在完成身份验证后获取一个短期、窄权限范围的令牌。它会过期。它只能执行已验证用户被允许执行的操作。它不是万能密钥。
  • 完全不用密钥。 如果数据是公开的,就无需身份验证直接提供。如果不是公开的,就不要提供给未经验证的 JavaScript。

在客户端代码中硬编码 API 密钥,等于把家门钥匙放在门垫下面,同时公开您的住址。

凭证生命周期问题

这个 ClickUp 密钥很可能只创建过一次,粘贴进一个 JavaScript 文件,提交到仓库,部署到生产环境,之后再没人想起它。没有人轮换它。没有人限定它的权限范围。没有人设置有效期。没有人监控它访问了什么。

这就是大多数组织中大多数 API 密钥的生命周期。匆忙创建,粘贴到需要的地方,然后被遗忘。它们在代码库、配置文件、CI/CD 流水线中不断累积——显然还包括页面源码——每一个都是一扇永远不会上锁的门。

问题不在于您的组织是否存在这样的密钥。问题在于您有多少个,以及如果今天有人复制了其中一个,您是否能够察觉。