用Algolia DocSearch做文档搜索的前端项目多了去了。Vue.js、Home Assistant、KEDA……这些你每天查文档的站点,搜索框背后的API Key可能正以admin权限暴露在前端代码里。
上周一个安全研究员Ben Zimmermann公布了他的发现:扫描了15000个文档站点后,找到39个拥有完整admin权限的Algolia API Key。这些Key能干什么?添加、修改、删除索引记录,甚至直接把整个搜索索引清空。
DocSearch的机制和问题根源
Algolia的DocSearch是个免费服务——它帮开源项目爬站点、建索引、提供搜索。接入方式很简单,前端嵌一段JS,传入appId和apiKey就能用。
正常流程是这样的:Algolia给你两个Key,一个search-only用于前端,一个admin用于爬虫和索引管理。前端代码里只该出现search-only Key。
问题出在”自建爬虫”这条路上。DocSearch官方有个run your own crawler的文档,很多团队选择自己跑爬虫。一跑就需要admin Key,然后这个Key就在配置文件里待着了。有人图省事直接把admin Key塞进前端配置,有人把Key提交到了Git历史里。
典型的前端接入代码长这样:
docsearch({
appId: 'BH4D9OD16A',
apiKey: '5990ad008512000bba2cf951ccf0332f', // 这个该是search-only
indexName: 'my-docs',
container: '#search'
})
search-only Key和admin Key长得一模一样——都是32位十六进制字符串。肉眼看不出区别,只有在Algolia后台的权限页面才能确认。这就是问题所在:很多开发者根本没验证过自己前端用的到底是哪个Key。
怎么找到这些Key的
Ben的方法分三步,每一步都值得前端开发者了解。
第一步:从DocSearch配置库批量抓目标。Algolia官方有个已归档的docsearch-configs仓库,里面收录了3500+个使用DocSearch的站点配置。这就是现成的扫描目标列表。
第二步:前端代码扫描。用正则从网页源码中提取Algolia凭据:
import re
# appId是10位大写字母+数字
APP_RE = re.compile(r'["']([A-Z0-9]{10})["']')
# apiKey是32位十六进制
KEY_RE = re.compile(r'["']([\da-f]{32})["']')
# 先确认页面包含Algolia相关代码
ALGOLIA_RE = re.compile(r'algolia|docsearch', re.I)
def extract(text, app_ids, api_keys):
if not ALGOLIA_RE.search(text):
return
for a in APP_RE.findall(text):
app_ids.add(a)
api_keys.update(KEY_RE.findall(text))
39个admin Key中有35个来自前端代码扫描。这些Key没有出现在任何Git仓库里——它们是构建时注入的,只有在部署后的网页源码中才能看到。
第三步:Git历史挖掘。对500多个文档站点的代码仓库跑TruffleHog,从Git历史中挖出被提交后又删除的Key。另外4个admin Key就是这么找到的。
受影响的项目有多大
部分受影响项目(数据来自原文):
| 项目 | GitHub Stars | 说明 |
|---|---|---|
| Home Assistant | 85,000+ | 数百万活跃安装 |
| Vue.js | 48,000+ | 已修复并轮换Key |
| KEDA | CNCF项目 | 生产级Kubernetes集群在用 |
| vcluster | Kubernetes基础设施 | 搜索索引超10万条记录 |
| SUSE/Rancher | 企业级 | 2天内响应并轮换 |
Vue.js是最先被发现的。Ben去年10月就报告了,Vue团队轮换了Key,并把他加进了安全名人堂。问题是这激发了他的好奇心——既然Vue有这问题,别的项目呢?
这些Key能造成什么破坏
几乎所有39个Key都有同一组权限:search、addObject、deleteObject、deleteIndex、editSettings、listIndexes、browse。部分Key甚至额外包含analytics和logs权限。
拿到这种Key可以:
const algoliasearch = require('algoliasearch')
const client = algoliasearch('APP_ID', 'LEAKED_ADMIN_KEY')
const index = client.initIndex('docs')
// 1. 往搜索索引里插入恶意记录
await index.saveObject({
objectID: 'malicious-1',
title: 'Quick Start Guide',
url: 'https://phishing-site.com/install.sh', // 钓鱼链接
content: 'Run this command to get started...'
})
// 2. 修改已有记录的链接
await index.partialUpdateObject({
objectID: 'existing-doc-123',
url: 'https://evil-cdn.com/compromised-package.tar.gz'
})
// 3. 核弹选项——直接删掉整个索引
await index.delete()
想象一下:某个知名开源项目的文档搜索结果被替换成钓鱼链接。用户搜”安装指南”,点进去看到的是一段恶意脚本。这不是理论攻击——前面的RAG投毒本质上就是类似的思路,只不过Algolia这条路更简单粗暴。
前端开发者该怎么检查自己的项目
如果你的项目用了Algolia或DocSearch,现在就该检查。
方法一:查看Algolia控制台
登录Algolia后台 → Settings → API Keys。找到你前端代码里用的那个Key,看它的ACL(Access Control List)。如果只有search权限,没问题。如果你看到addObject、deleteObject、deleteIndex这些权限——立即轮换。
方法二:用API直接检测
# 用你前端的appId和apiKey测试写入权限
curl -s -X POST "https://APP_ID-dsn.algolia.net/1/indexes/test_probe/query" -H "X-Algolia-Application-Id: APP_ID" -H "X-Algolia-API-Key: YOUR_FRONTEND_KEY" -d '{"query":"test"}'
# 如果下面这个请求成功了,说明Key有写权限——危险!
curl -s -X POST "https://APP_ID.algolia.net/1/indexes/test_probe" -H "X-Algolia-Application-Id: APP_ID" -H "X-Algolia-API-Key: YOUR_FRONTEND_KEY" -H "Content-Type: application/json" -d '{"objectID":"probe","test":true}'
# 成功=你的Key权限太大了
# 403/401=Key是search-only,安全
方法三:扫描你的构建产物
#!/bin/bash
# 在构建产出目录里扫描可能的Algolia凭据
echo "扫描构建产出中的Algolia Key..."
# 找所有JS文件中的32位十六进制字符串
grep -rPoh '["'][0-9a-f]{32}["']' dist/ build/ .next/ 2>/dev/null | sort -u | while read key; do
clean=$(echo "$key" | tr -d ""'")
echo "发现候选Key: $clean"
echo " → 请到Algolia后台确认该Key的权限级别"
done
正确的接入方式
如果你要自建DocSearch爬虫,Key的管理要做到分离:
# .env(爬虫用,绝对不提交到Git) ALGOLIA_ADMIN_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx # 前端配置(可以提交,可以暴露) NEXT_PUBLIC_ALGOLIA_SEARCH_KEY=yyyyyyyyyyyyyyyyyyyyyyyy NEXT_PUBLIC_ALGOLIA_APP_ID=XXXXXXXXXX
// 前端代码——只用search Key import algoliasearch from 'algoliasearch/lite' // 注意用lite版 const searchClient = algoliasearch( process.env.NEXT_PUBLIC_ALGOLIA_APP_ID, process.env.NEXT_PUBLIC_ALGOLIA_SEARCH_KEY // search-only! )
几个硬性规则:
- 前端永远用
algoliasearch/lite——这个包本身就只支持搜索操作,就算Key权限大也限制了客户端能调用的API .env里的admin Key加到.gitignore,CI/CD里用Secret注入- Algolia后台定期Review API Key列表,删掉不用的Key
- 如果用GitHub Actions跑爬虫,admin Key放在Repository Secrets里,别写进workflow YAML
Algolia的态度让人失望
Ben给Algolia发了完整的Key列表和受影响项目清单。几周过去了,没有回复。截至文章发布时,除了Vue.js和SUSE/Rancher主动修复的之外,其余Key全部仍然有效。
这其实暴露了一个更深层的问题:Algolia的DocSearch项目设计上没有做足防错。它完全依赖用户自己区分search Key和admin Key,但这两种Key在格式上毫无差别。一个简单的改进是:在Dashboard上对admin Key加醒目警告”此Key不应用于前端”,或者在检测到admin Key被用于搜索请求时发邮件告警。
对前端开发者来说,教训很明确:任何出现在前端代码、构建产物、或公开仓库中的API Key,必须是最小权限的。不只是Algolia——Firebase、Supabase、Stripe publishable key、Mapbox token,所有前端可见的凭据都该检查一遍权限范围。
原始研究来源:I Found 39 Algolia Admin Keys Exposed Across Open Source Documentation Sites