Vibe Tutorial
环境变量与安全机制

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. 本章小结

  1. 🔒 物理隔离:Server是保险箱,Client是展示柜,秘密绝对不能出保险箱
  2. 🛡️ 默认安全:Next.js默认不让Client读取.env,这是为了保护你
  3. 🔄 API中转:前端需要调用敏感服务时,必须通过后端的API Route进行"洗白"转发
  4. 📢 公开变量:如果真的需要在前端使用,必须以NEXT_PUBLIC_开头命名