详解前端暴露API密钥的危害,演示用Cloudflare Workers实现BFF代理模式安全调用Groq/OpenAI,避免密钥泄露和滥用。
我们每个人都见过这种情况:打开某个"前沿"AI 初创公司落地页的浏览器 DevTools,查看 Network 标签,发现一个直接 POST 请求到 api.openai.com,请求头中赫然包含明文的 API Key。
这是现代 Web 开发中最常见——也最危险——的错误之一。在客户端暴露 LLM API Key 等于广发请帖,迟早会被滥用,导致积分被盗、账单暴涨,甚至账号被封禁。
标准解决方案是 Backend-for-Frontend(BFF)代理模式。但如何在不启动笨重的 Express 服务器的前提下,实际地、廉价地、安全地实现它?
本指南将带你构建一个轻量、无服务器的 AI 代理,使用 Cloudflare Workers 来安全地调用 Groq(或 OpenAI)API,为你的浏览器端计算器和工具提供服务。
我们的前端不再直接与 AI 提供商通信,而是引入一个无状态中间件层:
Browser App → Cloudflare Worker (Proxy) → Groq/OpenAI API
↑ ↑
(No API Key) (API Key stored securely in Worker env vars)
Worker 的职责:
接收来自前端的净化后的计算上下文(数字,而非个人隐私信息)。 通过环境变量附加加密的 API Key。 将请求转发给 LLM 提供商。 将生成的洞察以流式或直接返回的方式传回客户端。
我们将使用新的 create-cloudflare CLI。请确保已安装 Node.js。
npm create cloudflare@latest ai-proxy
选择 "Hello World" worker 和 TypeScript。进入目录后,安装 Groq SDK:
npm install groq-sdk
永远不要硬编码密钥。Cloudflare Workers 可以安全地暴露环境变量。
更新你的 wrangler.toml:
name = "ai-proxy"
main = "src/index.ts"
compatibility_date = "2024-12-18"
[vars]
GROQ_API_KEY = "your-secret-key-here" # Replace, but prefer using wrangler secret for production
生产环境下,将其设置为真正的 secret 以在仪表板中隐藏:
npx wrangler secret put GROQ_API_KEY
我们需要一个人口端点,接收 POST 请求,验证输入,调用 Groq,并返回结果。
以下是完整的 src/index.ts 实现:
import { Groq } from "groq-sdk";
export interface Env {
GROQ_API_KEY: string;
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
// 1. CORS Preflight (handle OPTIONS)
if (request.method === "OPTIONS") {
return new Response(null, {
headers: {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "POST, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type",
},
});
}
// 2. Only allow POST requests
if (request.method !== "POST") {
return new Response(JSON.stringify({ error: "Method not allowed" }), {
status: 405,
headers: { "Content-Type": "application/json" },
});
}
try {
// 3. Parse and sanitize input
const body = await request.json();
const { context, promptType } = body;
if (!context || typeof context !== "string") {
return new Response(
JSON.stringify({ error: "Missing 'context' field" }),
{ status: 400, headers: { "Content-Type": "application/json" } }
);
}
// 4. Initialize Groq with the secret key from environment
const groq = new Groq({
apiKey: env.GROQ_API_KEY,
});
// 5. Construct a system prompt based on the type (e.g., finance, health)
let systemPrompt = "You are a helpful financial assistant.";
if (promptType === "health") {
systemPrompt =
"You are a medical disclaimer assistant. Provide general wellness info only, no diagnoses.";
}
// 6. Call the LLM
const chatCompletion = await groq.chat.completions.create({
messages: [
{ role: "system", content: systemPrompt },
{
role: "user",
content: `Explain what these calculations mean in plain English: ${context}`,
},
],
model: "llama3-8b-8192", // Fast and cheap Groq model
temperature: 0.5,
max_tokens: 200,
});
const reply = chatCompletion.choices[0]?.message?.content || "No response generated.";
// 7. Return the response to the browser with CORS headers
return new Response(
JSON.stringify({ success: true, data: reply }),
{
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
},
}
);
} catch (error) {
console.error("Proxy error:", error);
return new Response(
JSON.stringify({ error: "Internal server error" }),
{ status: 500, headers: { "Content-Type": "application/json" } }
);
}
},
};
现在,回到你的浏览器端计算器(Vanilla JS、React 或 Vue),你只需调用已部署的 Worker URL。
以下是最简前端 fetch 示例:
async function getAIInsight(calculationResult, type) {
try {
const response = await fetch("https://your-worker-name.workers.dev/api/ai", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
context: `Principal: $1000, Rate: 5%, Years: 10. Future Value: $1628.89.`,
promptType: type, // e.g., "finance"
}),
});
if (!response.ok) throw new Error("Network error");
const json = await response.json();
return json.data; // The AI explanation
} catch (error) {
console.error("Failed to fetch AI insight:", error);
return "Insight unavailable at this moment.";
}
}
至关重要的一点:注意前端根本不知道 API Key。即使攻击者检查这个网络请求,他们看到的也只是对你 Worker 的调用,而非对 Groq/OpenAI 的调用。
虽然这个模式非常有效,但它并非魔法。需要关注以下几点:
| 因素 | 注意事项 |
|---|---|
| 延迟 | 引入代理会增加一个网络跳转。借助 Cloudflare 的全球网络,这通常小于 50ms,但还是值得测量的。 |
| 成本 | Cloudflare Workers 有慷慨的免费套餐(每天 100k 请求)。然而,你仍需为 LLM token 付费。记得设置严格的 max_tokens 限制。 |
| 速率限制 | 没有账号,如何防止滥用?你可以使用 Workers KV 实现简单的基于 IP 的速率限制器,防止单一 IP 耗尽你的额度。 |
| CORS | 如果你的前端在特定域名上,请将 Access-Control-Allow-Origin 限制在该域名,而非在生产环境使用 *。 |
这个确切架构目前正在 AfriWidget.com 上生产运行。他们使用 Groq 代理为数复利计算器和 GPA 计算器提供 AI 解释。
前端只发送数值上下文——例如 { principal: 5000, rate: 7, years: 20 }——到代理。Worker 附加系统 prompt,调用 Groq 的 Llama 3 模型,然后将财务预测的通俗英文解释流式返回。
值得注意的是,他们在代理日志中不存储用户输入或姓名,确保即使 Worker 日志被泄露,也不会泄露任何个人身份信息(PII)。
在客户端代码中暴露 API Key 是一条捷径,最终会成为财务负担。花 15 分钟搭建一个 Cloudflare Worker,你可以获得:
代理模式不仅仅适用于 AI API。把它应用到任何需要密钥的第三方服务上。未来的你——以及你的银行账户——会感谢现在的自己。
你使用什么模式来保护外部 API 调用?在评论区告诉我,或者分享你的 Worker 实现!