如何安全存储和使用API Secret的实战经验分享
先看代码,再扯别的
我最近在搞一个前端项目,需要调用第三方地图 API,结果一上来就卡住了——API Secret 怎么处理?直接写进代码里?那不是谁扒开浏览器 DevTools 都能偷走?可不写进去,又没法发请求。折腾了半天,最后用了个折中但靠谱的方案,今天就来聊聊我踩过的坑和亲测有效的做法。
先上最简单的场景:你有个前端页面要调用某个 API,比如获取天气数据:
fetch('https://jztheme.com/api/weather?city=beijing&api_secret=abc123xyz')
.then(res => res.json())
.then(data => console.log(data));
别笑,我知道这写法很 naive。但我敢说,至少一半刚入行的人(包括曾经的我)都这么干过。问题是,api_secret 完全暴露在前端代码里,别人只要打开 Network 面板,就能复制你的 key 去乱发请求,轻则被限流,重则账单爆炸。
真正的解法:后端代理 + 环境变量
正确的姿势是:前端不碰 secret,所有带 secret 的请求都走自己的后端做个“中间人”。
比如我在 Node.js 写了个简单接口:
// server.js
require('dotenv').config();
const express = require('express');
const axios = require('axios');
const app = express();
app.get('/api/weather', async (req, res) => {
const { city } = req.query;
try {
const response = await axios.get('https://jztheme.com/api/weather', {
params: {
city,
api_secret: process.env.MAP_API_SECRET // 从环境变量读
}
});
res.json(response.data);
} catch (error) {
res.status(500).json({ error: '请求失败' });
}
});
app.listen(3000);
然后前端只管调自己后端:
fetch('/api/weather?city=shanghai')
.then(res => res.json())
.then(data => console.log(data));
这样前端完全看不到 secret,它只和自己的后端通信。而真正的 secret 存在 .env 文件里:
MAP_API_SECRET=abc123xyz
别忘了把 .env 加到 .gitignore,不然 commit 上去等于公开密码。
部署时的坑:Vercel、Netlify 怎么配环境变量?
本地跑没问题,但部署就容易翻车。比如我第一次用 Vercel 部署时,死活拿不到 process.env.MAP_API_SECRET,查了好久才发现——Vercel 的环境变量得在界面上手动填。
步骤如下:
- 进项目设置 → Environment Variables
- 加个 Key:
MAP_API_SECRET,Value 填你的密钥 - 重新部署
Netlify 也一样,在 Settings → Environment variables 里配。
这里注意下,我踩过好几次坑:有时候你以为变量生效了,其实是缓存或者旧构建还在跑。建议每次改完变量,手动 trigger 一次新 deploy,别等自动同步。
那能不能前端直接用?非要用怎么办?
有人会问:我就一个小 demo,懒得搭后端,能不能让前端用,但至少防君子不防小人?
答案是:有,但效果有限。
比如你可以把 secret 存在 sessionStorage 里,通过一个登录接口动态注入:
// 用户登录后,后端返回临时 token(不含 api_secret)
fetch('/api/login', { method: 'POST', body: formData })
.then(res => res.json())
.then(({ tempToken }) => {
sessionStorage.setItem('auth', tempToken);
// 然后用这个 token 换取实际的 api 调用资格
});
但这只是转移问题,没解决问题。真想防,还是得走代理。
还有一种常见错误做法:把 secret 拆成两半,拼在 JS 里:
const part1 = 'abc123';
const part2 = 'xyz';
const secret = part1 + part2; // abc123xyz
别闹了,这种 obfuscate 对现代浏览器来说就是明文。
CORS 和代理配置的小细节
做反向代理时,经常遇到 CORS 报错。比如前端域名是 https://myapp.com,后端是 https://api.myapp.com,得确保后端允许跨域。
Express 可以这样配:
const cors = require('cors');
app.use(cors({
origin: ['https://myapp.com'],
credentials: true
}));
但如果前端和后端同源(比如都部署在 Vercel,前端是根路径,API 走 /api/xxx),那就根本不会有跨域问题,推荐这种结构。
高级点的玩法:JWT + 短期凭证
如果你的系统用户量大,还可以玩更安全的:用 JWT 签发短期访问凭证。
流程大概是:
- 用户登录,后端验证身份
- 后端生成一个短期 token(比如 5 分钟有效期),里面不包含 secret,只说明“这个用户允许调用地图 API”
- 前端拿着这个 token 去请求代理接口
- 代理接口收到后,用自己的 secret 发真实请求,把结果返回
好处是:即使前端 token 被截获,也很快过期,而且不能直接用来调第三方 API。
实现上可以用 jsonwebtoken 库:
const jwt = require('jsonwebtoken');
// 生成 token
const token = jwt.sign(
{ userId: 123, permissions: ['map'] },
process.env.JWT_SECRET,
{ expiresIn: '5m' }
);
然后在中间层验证:
function verifyToken(req, res, next) {
const auth = req.headers.authorization;
if (!auth) return res.status(401).json({ error: '未授权' });
const token = auth.split(' ')[1];
jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
if (err) return res.status(403).json({ error: 'token无效或过期' });
req.user = user;
next();
});
}
这套机制稍微重一点,但适合中大型应用。
踩坑提醒:这三点一定注意
1. 永远不要在 GitHub 搜 “api_secret” 或 “apikey” —— 我试过,搜一下全是别人的泄漏 key,有些还能用。你自己别成为别人搜索结果里的那个倒霉蛋。
2. 本地调试时,.env 文件权限设为 600,避免其他用户读取。Linux/Mac 下可以用:
chmod 600 .env
3. 监控 API 调用量。我之前没设监控,结果某天发现请求量暴涨 10 倍,查日志发现是某个测试 key 被泄露了。现在我都给每个 key 加前缀,比如 dev_、prod_,方便追踪。
总结一下我现在的标准做法
我现在新项目统一这样搞:
- 所有第三方 API 请求走后端代理
- secret 存在环境变量,CI/CD 里配好
- 本地用
dotenv,生产用平台提供的 env 管理 - 敏感接口加 rate limiting,比如每个 IP 每分钟最多 10 次
- 定期轮换 secret,尤其是团队有人离职时
改完之后仍然有一两个小问题,比如本地开发要多跑一个 server,但比起安全风险,这点麻烦不算啥。
这个技巧的拓展用法还有很多,比如结合 OAuth、API Gateway 做更精细的权限控制,后续会继续分享这类博客。以上是我踩坑后的总结,希望对你有帮助。有更优的实现方式欢迎评论区交流。

暂无评论