文章总结: 研究人员扫描2200万个云开发平台公开项目,发现8792个有效敏感凭证持续暴露,包括能写入GitHub核心生产代码库的员工令牌。文章揭示CodeSandbox等平台因缺乏密钥扫描机制成为安全盲区,并指出平台功能越强泄露凭证越敏感的规律。建议开发者审计公开项目并采用环境变量管理,平台需借鉴GitHub的推送保护和自动吊销机制。 综合评分: 92 文章分类: 云安全,数据泄露,漏洞分析,安全运营,供应链安全
云开发环境里,藏着成千上万个“活”密码
幻泉之洲
2026年4月23日 09:32 北京
在小说阅读器读本章
去阅读
研究人员扫描了2200万个公开项目,在四个主流云开发平台(CodeSandbox、StackBlitz、CodePen、JSFiddle)上发现了8792个已验证的、仍在生效的敏感凭证。这不是单纯的统计数字,它意味着真实的权限泄漏,包括一个能写入GitHub核心生产代码库的员工令牌。文章揭露了这些“盲区”环境的安全隐患,并分析了问题根源和可能的防护方案。
被忽视的盲区
大家说到密钥泄露、代码扫描,第一反应都是GitHub、GitLab这些托管平台。毕竟它们功能完善,有推送保护(push protection),还有一堆合作伙伴帮你自动撤销泄露的凭证。
但还有一类开发平台,几乎没人系统性地关注过:云端开发环境。
什么是云端开发环境?就是像CodeSandbox、StackBlitz、CodePen、JSFiddle这样的东西。让你能在浏览器里直接写代码、跑应用。用来搞原型设计、学习、分享Demo,甚至搭完整项目。
关键区别在于,这些地方天生就没那些防护。没有原生的密钥扫描,没有推送保护,也没有合作伙伴会自动帮你撤销泄露的密钥。开发者把一个API密钥贴到公共项目里,这东西就杵在那儿了,除非他手动删掉,或者修改可见性。
这不就成公开的秘密了?是的。
这次研究就想弄明白一件事:云端开发环境是不是在大规模泄露凭证?答案是肯定的。
CodeSandbox的情况最突出,平均每1299个沙盒里就有一个验证为真的密钥。这也不奇怪,在这四个平台里,它功能最全,经常被用来构建完整应用,会涉及后端服务、环境文件和第三方集成。很多人甚至把它当成私有工作区来用,哪怕项目是公开的。
CodePen的泄露密度最低。这也合理,Pens通常是前端代码片段,不太需要后端密钥。但架不住它量大,有将近一千万个Pens,密度再低也揪出了近千个仍在生效的密钥。
根据CodeSandbox的创建时间数据,最早一个带有效密钥的沙盒可以追溯到2018年4月。数据还显示,密钥泄露的数量在逐年增加,2025年验证出的秘密是2024年的两倍多。
怎么找到2200万个公开项目?
这事儿不简单。没有哪个平台会直接给你一个“列出所有项目”的API,得靠点“花招”。
CodeSandbox:利用公共索引
我发现它用了一个公开的Algolia搜索索引来提供搜索功能。这个索引包含了每一个公共沙盒的元数据,包括ID和创建时间戳。于是写了个脚本,按天查询这个索引,通过分页拿到平台上的所有公共沙盒。
def enumerate_day(day, *, query=””, hits_per_page=1000, session=None): start_ts, end_ts = _timestamp_bounds(day) params = { “x-algolia-application-id”: ALGOLIA_APPLICATION_ID, “x-algolia-api-key”: ALGOLIA_API_KEY, } hits = [] for page in range(MAX_PAGES): encoded = _build_params(query, page, hits_per_page, start_ts, end_ts) response = sess.post( ALGOLIA_URL, params=params, json={“requests”: [{“indexName”: ALGOLIA_INDEX, “params”: encoded}]}, ) page_hits = response.json()[“results”][0].get(“hits”, []) hits.extend(page_hits) if len(page_hits) < hits_per_page: break return hits
就这样,我拿到了8,362,053个沙盒ID,然后通过CodeSandbox的API下载每个沙盒的源代码。
CodePen:社交图谱爬取
这个没公共索引可用。我换了个思路:爬社交图谱。
先把CodePen趋势榜和搜索页背后的GraphQL端点抓下来,用它们收集一批初始用户,然后递归爬取每个用户的粉丝和关注列表。像滚雪球一样,最后扩到57万个独立用户。再从这些用户那儿拉取每一个公开的Pen,总共10,296,169个。
def _expand_followers(self, owner_id, cursor): payload = followers_payload(owner_id, self.follower_limit, cursor) resp = self.session.post(self.cfg.graphql_url, json=payload, timeout=20) data = resp.json().get(“data”, {}).get(“ownerFollowers”, {}) owners = data.get(“owners”, []) or [] next_cursor = data.get(“pageInfo”, {}).get(“cursorEnd”) inserted = self.storage.add_owners( (entry[“id”], entry.get(“username”, “”), f”followers:{owner_id}”) for entry in owners if entry.get(“id”) ) if not next_cursor: self.storage.mark_followers_complete(owner_id) else: self.storage.update_follower_cursor(owner_id, next_cursor)
JSFiddle & StackBlitz:用户名交叉比对
这两个既没公共索引,也没社交图谱。我只能用GitHub用户名当桥。
开发者经常在不同平台复用同一个用户名。所以我从BigQuery的公共GitHub数据集里拉了720万个GitHub用户名,挨个去这两个平台的个人主页试。用户名有效的话,对于JSFiddle就调用其公开API列出并下载用户的fiddles,最后得到608,258个。对StackBlitz,用同样的方法枚举了3,014,469个项目。
全算下来,我从四个平台总共枚举并下载了超过2220万个项目。
扫描和分析:从海量代码到关键风险
所有内容先下载到本地服务器,然后用TruffleHog配上--only-verified标志进行扫描。为了能高效下载几百万个项目,我还用上了代理轮换。
if proxies: sessions = [] for proxy in proxies: s = requests.Session() s.proxies = {“http”: proxy, “https”: proxy} adapter = HTTPAdapter(pool_connections=workers, pool_maxsize=workers) for scheme in (“https://”, “http://”): s.mount(scheme, adapter) sessions.append(s) _rr = cycle(sessions) _rr_lock = threading.Lock() def next_session(): with _rr_lock: return next(_rr)
扫描完,去重,最后确认有8792个独一无二的有效密钥,涉及几十家云厂商和SaaS服务商。
但这只是第一步。接下来得分析,哪些是关键风险。麻烦的是,和Git平台不同,这些开发环境一般不暴露提交者邮箱等元数据,很难把密钥追溯到具体的个人或组织。
怎么处理?靠大语言模型辅助。我用Claude Code写Python脚本,自动抓取每个密钥的元数据,比如账户信息、权限范围、可访问的资源、组织详情等等。
提示词大概是:“生成一个结构化Python脚本,读取{service}.jsonl文件作为输入,调用{service}的API拉取元数据(调用者身份、权限范围、可访问资源、组织信息),然后流式输出到文件。用Firecrawl MCP参考{service}的API文档确保最佳实践。”
脚本跑出来的结构化数据,再喂给本地运行的gpt-oss-20b模型。让模型帮我从海量数据里揪出值得手动深挖的发现,比如有管理员权限的令牌,或者关联到大组织的凭证。所有被标记出来的,最终都要我手动核实一遍才启动披露流程。
最惊人的发现:能写入GitHub.com的令牌
在CodeSandbox上,我发现一个公共沙盒里有个GitHub员工的OAuth令牌,就在一个index.ts文件里。
这个令牌有repo、workflow、codespace、gist和read:org权限。我拿去测试GitHub API,返回结果确认它拥有对github/github仓库的推送权限——这个私有仓库里装的,就是GitHub.com的生产源代码。
github/github这个仓库,ID是3,2007年10月29号创建。这个令牌能访问超过26个组织里的74000多个仓库,包括Microsoft、Azure、GitHub Actions,还有GitHub内部的早期访问和面试用组织。有了写权限,还加上工作流权限,攻击者理论上可以修改GitHub Actions流水线,向生产代码库注入恶意代码,甚至发起下游供应链攻击。
我通过HackerOne上的GitHub漏洞赏金计划报告了这事。GitHub处理了问题,并给了20000美元的赏金。
其他重要发现
在另一个平台,我发现了一个家得宝员工的GitHub个人访问令牌。它拥有对64个仓库的管理员权限,对总共664个仓库有推送权限,涉及内部基础设施、认证系统和密钥管理。这个令牌已经公开暴露了大概一年。
还有一个例子,在另一个平台找到一个SSH私钥,能认证为一名红帽员工,对eclipse-che/che仓库有写权限——这仓库是红帽OpenShift开发者空间的直接上游。
我执行了ssh -i key [email protected] git-receive-pack eclipse-che/che.git来验证能力。结果呢?
GitHub没有返回只读错误,而是直接给出了完整的引用公告,包括删除引用的能力。这确认了该密钥确实可以推送和修改这个仓库。拿到这个密钥的攻击者,完全可能把恶意代码塞进红帽的商业产品里。我把这事儿报给了红帽和Eclipse基金会,红帽后来把我列入了他们的安全致谢页面。
问题在哪,怎么破?
首先,得承认,云端开发环境就是个安全盲区。我扫描的四个平台,都没有密钥扫描、推送保护或者合作伙伴凭证吊销程序。你贴个API密钥上去,没有自动检测,也没有自动撤销。
然后是披露的难题。要协调几十家SaaS供应商和四个开发平台来吊销凭证,需要自动化、批量联系,还要直接跟安全团队打交道。说实话,没有Truffle Security帮忙居中协调供应商,这里面大部分的凭证到现在恐怕还好端端地挂着。
还有个有意思的现象:泄露的密钥类型,直接跟平台的功能挂钩。
CodeSandbox允许完整的后端环境,所以找到的往往是高价值凭据:数据库连接串、云密钥、服务账号。CodePen只做前端,泄露的大多是天气、地图这类服务的公开API密钥。平台功能越强,在上面找到的凭证就越敏感。
说到底,规律很一致:只要开发者写代码的地方,就少不了密钥。Git平台已经开始建防御工事了,但云端开发环境这方面还基本是空白。
对开发者来说,现在就能做的是,赶紧去审计一下你在这些平台的公开项目,把可能暴露的凭据都换掉。以后记住,对待公开沙盒,要和对待公开的GitHub仓库一样谨慎。千万别往里贴真实密钥,尽量用平台内置的环境变量UI(如果它有的话),分享前务必检查项目可见性。
对于开发环境平台自身,Git平台上已经解决了的问题——比如发布时扫描密钥、推送保护、合作伙伴吊销程序——完全可以借鉴过来。现在是时候行动了。
最后,这次大规模的扫描和披露,让我赚到了超过两万美元的赏金。但更重要的是,它揭示了一个被主流安全视野长期忽略的风险地带。
参考资料
[1] https://trufflesecurity.com/blog/thousands-live-secrets-found-across-four-cloud-dev-environments
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:幻泉之洲 《云开发环境里,藏着成千上万个“活”密码》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。











评论