环境变量与安全机制
6.2 Server vs Client 详解
Tip
理解Server和Client的区别,学会用API Route保护敏感信息,避免密钥泄露到前端。
1. 为什么要学这个?
你做了一个天气查询应用,在.env里配置了WEATHER_API_KEY=secret_123。在页面组件page.tsx里写道:
"use client";
console.log(process.env.WEATHER_API_KEY); // 输出: undefined
你很疑惑:明明配了,为什么读不到?
甚至,如果你强行把它传给前端,用户按一下F12,在Network里就能看到你的Key。
学会Server和Client的边界,才能既保护密钥,又让应用正常运行。
2. 核心概念
2.1 前店后厂
-
Server(服务端/后厨):
- 只有你可以进,闲人免进
- 这里存放着保险箱(数据库密码、支付私钥)
- 这里的代码运行在云端服务器上
-
Client(客户端/前店):
- 就是用户的浏览器
- 任何发送到这里的东西,用户都能看见(相当于把菜端上了桌)
- 这里的代码运行在用户的手机/电脑里
2.2 Next.js的保护机制
技术定义:Next.js严格区分Server Component(默认)和Client Component(
"use client")。私有变量默认的process.env.XXX只能在服务端读取,Next.js会阻止它们泄露到客户端包中。
3. 变量类型对比
| 变量类型 | 命名示例 | 存放位置 | 谁能看见? |
|---|---|---|---|
| 私有密钥 | DB_PASSWORD |
Server Only | 只有你(安全) |
| 私有API Key | OPENAI_API_KEY |
Server Only | 只有你(安全) |
| 公开ID | NEXT_PUBLIC_ANALYTICS_ID |
Client & Server | 全世界(公开) |
4. 数据如何安全穿透
sequenceDiagram
participant User as 用户(浏览器)
participant Server as 服务端(API Route)
participant 3rd as 第三方(OpenAI)
Note over User: ❌ 错误做法:前端直接调
User->>3rd: 请求(带Key)
Note right of User: 危险!用户F12看到了Key!
Note over User, 3rd: ✅ 正确做法:服务端中转
User->>Server: 1. 发送请求(我想问AI...)
Note over Server: Server从.env读取Key
Server->>3rd: 2. 携带Key转发请求
3rd-->>Server: 3. 返回结果
Server-->>User: 4. 返回结果(不含Key)
Note right of User: 安全!用户只看到结果
5. 代码实战
❌ 错误的写法(前端直连)
// app/page.tsx (Client Component)
"use client";
// ❌ 这行代码会让你的Key暴露给全世界
const response = await fetch("https://api.openai.com/...", {
headers: { Authorization: process.env.OPENAI_API_KEY } // 读不到,或者直接暴露
});
✅ 正确的写法(API Route中转)
第一步:在后端建一个接口(app/api/chat/route.ts)
import { NextResponse } from 'next/server';
export async function POST(req: Request) {
// ✅ 只有在这里(后端)才能安全读取私钥
const apiKey = process.env.OPENAI_API_KEY;
const result = await fetch("https://api.openai.com/...", {
headers: { Authorization: `Bearer ${apiKey}` }
});
return NextResponse.json(await result.json());
}
第二步:前端调用自己的后端
// app/page.tsx
const handleChat = async () => {
// ✅ 调用自己的API,不需要传Key
const res = await fetch("/api/chat", { method: "POST" });
};
6. 避坑指南
| ✅ 推荐做法 | ❌ 禁忌 |
|---|---|
| 凡是涉及钱/隐私的都在后端做 | 为了省事在前端拼接SQL语句(找死) |
| 前端只负责展示和交互 | 相信前端传来的数据(必须在后端二次校验) |
使用NEXT_PUBLIC_标记公开变量 |
把私钥加上NEXT_PUBLIC_前缀(这是把钥匙挂在大门上) |
7. 真实案例
Story
Twitter开发者API Key泄露(Android App)
研究人员通过反编译Twitter的Android客户端安装包(.apk),在里面发现了硬编码的Consumer Key和Secret。开发者误以为:"我把Key写在App代码里,打包成了二进制文件,用户就看不到了吧?"真相是:任何客户端代码都可以被反编译,任何硬编码的密钥都会被发现。
Vibe心法:客户端(Client)对黑客而言是透明的展示柜——任何关于钱和隐私的秘密,必须锁在服务端(Server)的高墙之内,利用API Route进行洗白中转。
8. 本章小结
- 🔒 物理隔离:Server是保险箱,Client是展示柜,秘密绝对不能出保险箱
- 🛡️ 默认安全:Next.js默认不让Client读取
.env,这是为了保护你 - 🔄 API中转:前端需要调用敏感服务时,必须通过后端的API Route进行"洗白"转发
- 📢 公开变量:如果真的需要在前端使用,必须以
NEXT_PUBLIC_开头命名