跳到正文
DEV Community· Thanawat Wongchai·· 2 小時前AI 評分65

如何在 2 分鐘內檢查 OpenAI、Claude 或 Gemini API Key 是否在瀏覽器洩漏

API Key รั่วไหลในเบราว์เซอร์? ตรวจสอบคีย์ OpenAI, Claude หรือ Gemini ของคุณใน 2 นาที

AI 導讀

網頁應用程式若直接從瀏覽器呼叫 OpenAI、Claude 或 Gemini,API Key 會暴露在網絡請求或前端程式碼中,可用 DevTools 檢查。文章建議將呼叫遷移至伺服器端,並加入用戶驗證、速率限制、供應商支出上限及 CI 掃描;部署新 Key 後再撤銷舊 Key。

正文

API Key รั่วไหลในเบราว์เซอร์? ตรวจสอบคีย์ OpenAI, Claude หรือ Gemini ของคุณใน 2 นาที

ถ้าเว็บแอปของคุณเรียก OpenAI, Claude หรือ Gemini ตรงจากเบราว์เซอร์ คีย์ API ของคุณจะถูกเปิดเผยในเบราว์เซอร์ และใครก็ตามที่เปิด DevTools ก็สามารถคัดลอกมันไปได้ คุณยืนยันได้ใน 2 นาที: เปิดแท็บ Network ส่งคำขอ AI หนึ่งครั้ง แล้วมองหาเฮดเดอร์ Authorization, x-api-key หรือ x-goog-api-key ที่ส่งไปยังผู้ให้บริการ ถ้าคุณเห็นคีย์ของคุณอยู่ตรงนั้น ให้หมุนเวียนคีย์วันนี้เลย แล้วย้ายการเรียกไปไว้หลังเส้นทางฝั่งเซิร์ฟเวอร์

ลองใช้ Apidog วันนี้

ด้านล่างนี้: วิธีตรวจสอบ ลำดับการแก้ไข และวิธีที่เอเจนต์เขียนโค้ดของคุณสามารถทดสอบเส้นทางที่แก้ไขแล้วได้

ทำไมคีย์ API ที่ถูกเปิดเผยในเบราว์เซอร์จึงเป็นปัญหา

คีย์ API ที่ถูกเปิดเผยในเบราว์เซอร์คือความลับที่ถูกส่งไปพร้อมกับโค้ดฝั่งฟรอนต์เอนด์หรือทราฟฟิกเครือข่ายของคุณ ซึ่งผู้เข้าชมทุกคนสามารถอ่านได้ ถ้าเบราว์เซอร์เป็นฝ่ายส่งคีย์ ผู้ใช้ก็สามารถคัดลอกมันไปและเรียกผู้ให้บริการโดยคิดเงินเข้าบิลของคุณ

คู่มือ Gemini API key guide ของ Google ระบุว่า "อย่าฮาร์ดโค้ดคีย์ API ลงในเว็บหรือแอปมือถือโดยตรง" เพราะ "คีย์ที่คอมไพล์เข้าไปในโค้ดฝั่งไคลเอนต์สามารถถูกผู้ใช้สกัดออกมาได้" และแนะนำให้ "รันเซิร์ฟเวอร์พร็อกซีแบ็กเอนด์เพื่อทำการเรียก API จริง" คู่มือ API key safety guide ของ OpenAI ก็ให้กฎเดียวกัน: ให้ส่งคำขอผ่านแบ็กเอนด์ของคุณเองแทนที่จะดีพลอยคีย์ไปที่ไคลเอนต์

README ของ openai-node ระบุว่าการรองรับเบราว์เซอร์ถูกปิดไว้โดยค่าเริ่มต้น "เพื่อหลีกเลี่ยงการเปิดเผยข้อมูลรับรอง API ลับของคุณ" และคุณต้องตั้งค่า dangerouslyAllowBrowser: true เพื่อเปิดใช้งาน TypeScript SDK ของ Anthropic ใช้ชื่อออปชันเดียวกัน และ API ของมันยอมรับการเรียกจากเบราว์เซอร์ (CORS) เฉพาะเมื่อคำขอมีเฮดเดอร์ anthropic-dangerous-direct-browser-access: true ตามที่ Simon Willison บันทึกไว้ตอนที่มันเปิดตัว

รูปแบบนี้พบได้บ่อยในแอปที่สร้างอย่างรวดเร็วด้วยเครื่องมือสร้างแอป AI และเอเจนต์เขียนโค้ด: เส้นทางที่สั้นที่สุดที่ใช้งานได้คือการ fetch จากคอมโพเนนต์ React โดยมีคีย์อยู่ในตัวแปร VITE_ หรือ NEXT_PUBLIC_ จากนั้นคีย์ที่ถูกขโมยจะถูกนำไปขายต่อสำหรับการประมวลผลจำนวนมาก ดังที่กล่าวถึงในโพสต์ของเราเรื่อง คีย์ AI API ที่ถูกขโมยและ LLMjacking และบิลของคุณคือที่ที่คุณจะรู้ตัว

วิธีตรวจสอบว่าคีย์ API ของคุณถูกเปิดเผยหรือไม่ (2 นาที)

Three-step infographic of the 2-minute key exposure check: 1 Network tab with a POST request highlighted, 2 search the JavaScript bundle for sk- and AIza, 3 check env vars prefixed NEXT_PUBLIC_ or VITE_

ให้รันการตรวจสอบเหล่านี้กับโปรดักชัน ไม่ใช่ localhost

  1. ดูแท็บ Network (45 วินาที) เปิดเว็บไซต์ของคุณ กด F12 (Cmd+Option+I บน Mac) เลือกแท็บ Network และเรียกใช้ฟีเจอร์ AI หนึ่งอย่าง เช่นส่งข้อความแชท กรองด้วย openai, anthropic หรือ googleapis ถ้ามีคำขอใดส่งตรงไปที่ api.openai.com, api.anthropic.com หรือ generativelanguage.googleapis.com ให้คลิกที่คำขอนั้นแล้วเปิด Headers การพบคีย์ใน [REDACTED CREDENTIAL],x-api-key,x-goog-api-keyหรือพารามิเตอร์คิวรี?key=` หมายความว่ามันถูกเปิดเผยแล้ว
  2. ค้นหาในบันเดิล (45 วินาที) ใน DevTools กด Cmd+Option+F (Ctrl+Shift+F บน Windows) เพื่อค้นหาในสคริปต์ทั้งหมดที่โหลดมา ค้นหา sk-, sk-ant-, AIza และ dangerouslyAllowBrowser หากพบในไฟล์ .js ที่ถูกเสิร์ฟให้เบราว์เซอร์ ถือว่ารั่วไหล แม้ว่าจะยังไม่มีคำขอใดถูกส่งออกไปก็ตาม
  3. ตรวจสอบคำนำหน้าของตัวแปรสภาพแวดล้อม (30 วินาที) เปิดไฟล์ .env และแดชบอร์ดโฮสติ้งของคุณ เครื่องมือบิลด์จะคัดลอกตัวแปรบางตัวเข้าไปในบันเดิลฝั่งไคลเอนต์โดยการออกแบบ: VITE_* ใน Vite (ดู เอกสาร env ของ Vite), NEXT_PUBLIC_* ใน Next.js, REACT_APP_* ใน Create React App และ EXPO_PUBLIC_* ใน Expo คีย์ของผู้ให้บริการที่อยู่หลังคำนำหน้าเหล่านี้ถือเป็นสาธารณะ ไม่ว่าจะตั้งชื่ออย่างไรก็ตาม

สำหรับการสแกนเอาต์พุตบิลด์ของคุณผ่านเทอร์มินัล:

`bash

from your project root after a production build

grep -rnoE "sk-(proj-|ant-)?[A-Za-z0-9_-]{20,}|AIza[0-9A-Za-z_-]{35}" dist/ build/ .next/static/ 2>/dev/null
grep -rn "dangerouslyAllowBrowser" dist/ build/ .next/static/ 2>/dev/null
`

เอาต์พุตว่างคือผ่าน Google ระบุว่าคีย์ AI Studio ใหม่ที่สร้างตั้งแต่ 28 พฤษภาคม 2026 เป็น "auth keys" ซึ่งรูปแบบอาจแตกต่างจาก AIza ดังนั้นให้ค้นหา generativelanguage.googleapis.com ในบันเดิลด้วย คุณจะผ่านก็ต่อเมื่อเบราว์เซอร์พูดคุยกับโดเมนของคุณเอง (ตัวอย่างเช่น /api/chat) และไม่มีชื่อโฮสต์ของผู้ให้บริการหรือคีย์ปรากฏขึ้นที่ใดเลย

ทำอะไรเมื่อคีย์ของคุณถูกเปิดเผย: ลำดับการแก้ไข

การหมุนเวียนคีย์ก่อนจะปิดช่องโหว่ของวันนี้ ส่วนการเปลี่ยนสถาปัตยกรรมจะปิดมันอย่างถาวร

ขั้นตอน สิ่งที่คุณทำ ทำไมต้องเรียงลำดับแบบนี้ เวลา
1. หมุนเวียนคีย์ สร้างคีย์ใหม่ในคอนโซลของผู้ให้บริการ แล้วเพิกถอนคีย์ที่รั่วไหล คีย์เดิมอาจถูกคัดลอกไปแล้ว การเพิกถอนคือสิ่งเดียวที่หยุดการใช้งานมันได้ 5 นาที
2. ย้ายการเรียกไปฝั่งเซิร์ฟเวอร์ วางการเรียกผู้ให้บริการไว้ในเส้นทางฝั่งเซิร์ฟเวอร์หรือเอดจ์ฟังก์ชันที่เก็บคีย์ไว้ หยุดไม่ให้คีย์ใหม่รั่วไหลในลักษณะเดียวกัน 30 ถึง 60 นาที
3. เพิ่มการยืนยันตัวตนและขีดจำกัดอัตราต่อผู้ใช้ กำหนดให้เส้นทางต้องมีเซสชัน และจำกัดจำนวนคำขอต่อผู้ใช้ ถ้าไม่มีสิ่งนี้ พร็อกซีของคุณจะกลายเป็นปลายทาง AI ฟรีสำหรับทุกคน 30 นาที
4. ตั้งขีดจำกัดการใช้จ่ายของผู้ให้บริการ ตั้งงบประมาณหรือขีดจำกัดการใช้จ่ายในการตั้งค่าการเรียกเก็บเงินของผู้ให้บริการแต่ละราย จำกัดบิลกรณีเลวร้ายที่สุดหากมีอย่างอื่นรั่วไหล 10 นาที
5. สแกนใน CI ทำให้บิลด์ล้มเหลวถ้ารูปแบบคีย์ปรากฏในบันเดิลเอาต์พุต ป้องกันไม่ให้ฟีเจอร์ถัดไปที่เอเจนต์เขียนขึ้นมาทำลายการแก้ไขนี้ 15 นาที

ให้ดีพลอยคีย์ใหม่ฝั่งเซิร์ฟเวอร์ก่อนที่คุณจะเพิกถอนคีย์เดิม ไม่งั้นฟีเจอร์ AI ของคุณจะล่ม คู่มือของเราเรื่อง แนวปฏิบัติที่ดีที่สุดในการหมุนเวียนคีย์ API ครอบคลุมการหมุนเวียนแบบไม่มีดาวน์ไทม์ ในขั้นตอนที่ 4 คำแนะนำด้านความปลอดภัยของคีย์จาก OpenAI แนะนำให้ตั้งขีดจำกัดการใช้จ่ายแบบบังคับใช้อย่างเข้มงวด เพราะการแจ้งเตือนเพียงอย่างเดียวไม่หยุดทราฟฟิก งบประมาณการเรียกเก็บเงินของ Google Cloud จะแจ้งเตือนคุณแต่ไม่หยุดการใช้งานด้วยตัวเอง

วิธีย้ายการเรียก OpenAI, Claude และ Gemini ไปไว้หลังเส้นทางฝั่งเซิร์ฟเวอร์

Diagram comparing an unsafe setup, where the browser holds the API key and calls the AI provider directly while DevTools copies the key, with a safe setup where the browser calls your server route, which holds the key and applies auth, rate limits and a spend cap before calling the provider

เส้นทางพร็อกซีแบ็กเอนด์คือเอนด์พอยต์เซิร์ฟเวอร์ขนาดเล็กที่รับพรอมป์ของผู้ใช้ เพิ่มคีย์ API ที่ฝั่งเซิร์ฟเวอร์ เรียกผู้ให้บริการ และส่งคืนเฉพาะคำตอบเท่านั้น เบราว์เซอร์จะไม่เห็นคีย์เลย นี่คือเส้นทาง Next.js แบบมินิมัลสำหรับ OpenAI ส่วน Claude และ Gemini ก็ใช้รูปแบบเดียวกัน:

`ts
// app/api/chat/route.ts (runs on the server only)
import OpenAI from "openai";
import { getSession } from "@/lib/auth"; // your auth helper
import { checkRateLimit } from "@/lib/limits"; // your per-user limiter

const client = new OpenAI({ [REDACTED CREDENTIAL] }); // no NEXT_PUBLIC_ prefix

export async function POST(req: Request) {
const session = await getSession(req);
if (!session) return new Response("Unauthorized", { status: 401 });

if (!(await checkRateLimit(session.userId, 20))) { // 20 requests per minute
return new Response("Too many requests", { status: 429 });
}

const { message } = await req.json();
if (typeof message !== "string" || message.length > 4000) {
return new Response("Bad request", { status: 400 });
}

const response = await client.responses.create({
model: process.env.OPENAI_MODEL!, // pin the model server-side
input: message,
});

return Response.json({ reply: response.output_text }); // return the answer, nothing else
}
`

สิ่งที่ทำให้มันปลอดภัย ไม่ใช่แค่ซ่อนไว้:

  • โมเดลอยู่บนเซิร์ฟเวอร์ ถ้าเบราว์เซอร์เป็นฝ่ายเลือกโมเดล ผู้โจมตีก็จะเลือกตัวที่แพงที่สุด
  • อินพุตถูกจำกัด ไม่มีพรอมป์ขนาดยักษ์ผ่านบัญชีของคุณ
  • ส่งกลับเฉพาะคำตอบ อย่าส่งคืนการตอบกลับดิบจากผู้ให้บริการหรือออบเจกต์ข้อผิดพลาด
  • ไม่มีคำนำหน้าสาธารณะ OPENAI_API_KEY อยู่ฝั่งเซิร์ฟเวอร์ ส่วน NEXT_PUBLIC_OPENAI_API_KEY จะถูกส่งไปให้ผู้เข้าชมทุกคน

สำหรับ Claude และ Gemini ให้ใช้ SDK ของพวกเขากับ ANTHROPIC_API_KEY หรือ GEMINI_API_KEY การจำกัดเว็บไซต์และ IP ของ Google ช่วยได้ แต่ใครก็ตามที่มี curl สามารถส่งเฮดเดอร์ Referer อะไรก็ได้ ดังนั้นให้มองมันเป็นชั้นที่สอง ส่วนกรณีที่สมเหตุสมผลกรณีเดียวสำหรับการเรียกจากเบราว์เซอร์คือเครื่องมือแบบ bring-your-own-key ที่ผู้ใช้แต่ละคนวางคีย์ของตัวเอง

ให้เอเจนต์เขียนโค้ดของคุณทดสอบเส้นทางพร็อกซีด้วย Apidog CLI

การย้ายคีย์เป็นเพียงครึ่งเดียวของงาน อีกครึ่งคือการพิสูจน์ว่ามันยังอยู่ที่เดิมหลังฟีเจอร์ถัดไปที่เอเจนต์ของคุณเขียนขึ้น ให้เอเจนต์ที่สร้างเส้นทางนั้นทดสอบมันด้วย Apidog CLI ตั้งค่าครั้งเดียว:

bash
npm install -g apidog-cli@latest
apidog login --with-token <YOUR_TOKEN>
npx -y skills add https://apidog.com # pick your agent when prompted

จากนั้นให้พรอมป์เดียวกับ Claude Code, Codex, Cursor หรือเอเจนต์ใดก็ตามที่คุณใช้:

`text
Create Apidog test cases in project for POST /api/chat on our staging
environment that assert:

  1. a request without a session cookie returns 401,
  2. a valid request returns 200 and the body does not contain "sk-", "sk-ant-", "AIza" or "api_key",
  3. the 21st request inside 60 seconds from the same user returns 429. Run them and summarize any failures. `

เอเจนต์จะอ่านสคีมา ตรวจสอบเพย์โหลดของมัน เขียนเคสทดสอบและรันมัน:

bash
apidog test-case category --project <id>
apidog cli-schema get test-case-create
apidog cli-schema validate test-case-create --file case.json
apidog test-case create --project <id> --file case.json
apidog run --test-case <caseId> --project <id> -e <envId> -r cli,json

การตรวจสอบข้อที่สองคือข้อที่สำคัญที่สุด: มันจะล้มเหลวทันทีที่ออบเจกต์ข้อผิดพลาดของผู้ให้บริการหรือฟิลด์ดีบักเริ่มสะท้อนคีย์ของคุณกลับไปยังไคลเอนต์ ให้ใส่ขั้นตอน apidog run ... -r junit เดียวกันนี้ในไปป์ไลน์ของคุณ เพื่อให้ทุกการดีพลอยตรวจซ้ำอีกครั้ง คู่มือ Apidog CLI CI/CD ของเราแสดงวิธีตั้งค่า และ Apidog CLI ใน Claude Code ครอบคลุมฝั่งเอเจนต์ แผนฟรีของ Apidog รองรับผู้ใช้สูงสุด 4 คน ทีมเล็กจึงสามารถทำสิ่งนี้ได้โดยไม่ต้องจ่ายค่าที่นั่ง

สำหรับปัญหาด้านเดียวกันในฝั่งรีโป คือคีย์ที่ถูกคอมมิตเข้า Git แทนที่จะถูกส่งไปยังเบราว์เซอร์ ดูโพสต์ของเราเรื่อง การตรวจสอบการรั่วไหลของคีย์ API ด้วยตัวสแกนความลับ ถ้าเอเจนต์ของคุณเรียกเครื่องมือที่จัดการข้อมูลลูกค้า การทดสอบเอเจนต์ AI เพื่อหาการรั่วไหลของข้อมูล ครอบคลุมชั้นถัดไป

ข้อผิดพลาดทั่วไปที่ทำให้คีย์ยังคงถูกเปิดเผย

  • การเข้ารหัสคีย์เป็น Base64 หรือแบ่งมันออกเป็นส่วน ๆ เบราว์เซอร์ต้องประกอบมันกลับเพื่อส่ง ดังนั้น DevTools จะแสดงค่าจริง
  • การย้ายมันไปไว้ใน localStorage หรือคุกกี้ มันยังอยู่บนไคลเอนต์อยู่ดี
  • การทำพร็อกซีโดยไม่มีการยืนยันตัวตน /api/chat ที่เป็นสาธารณะคือปลายทาง AI เปิดโล่งที่คิดเงินเข้าบิลของคุณ
  • การเก็บคีย์เดิมไว้ "เผื่อไว้" คีย์ที่เป็นสาธารณะแม้แค่วันเดียวก็ถูกคัดลอกไปแล้ว
  • การลืมแอปมือถือ คู่มือของ Google กล่าวถึง "เว็บหรือแอปมือถือ" รวมกันด้วยเหตุผลบางอย่าง

คำถามที่พบบ่อย

ฉันจะรู้ได้อย่างไรว่าคีย์ OpenAI API ของฉันถูกเปิดเผยในเบราว์เซอร์? ให้เรียกใช้ฟีเจอร์ AI โดยเปิดแท็บ Network ของ DevTools ไว้ คำขอไปที่ api.openai.com ที่มีเฮดเดอร์ Authorization: Bearer [REDACTED]-... หมายความว่ามันถูกเปิดเผยแล้ว ให้ค้นหาสคริปต์ที่โหลดมาสำหรับ sk- และ dangerouslyAllowBrowser ด้วย

การใส่คีย์ API ในตัวแปรสภาพแวดล้อม NEXT_PUBLIC_ หรือ VITE_ ปลอดภัยหรือไม่? ไม่ปลอดภัย คำนำหน้าเหล่านี้มีไว้เพื่อคัดลอกค่าต่าง ๆ เข้าไปในบันเดิลฝั่งไคลเอนต์ คีย์ของผู้ให้บริการใดก็ตามที่เก็บไว้ใต้คำนำหน้าเหล่านี้จะถูกส่งไปให้ผู้เข้าชมทุกคน ให้ใช้ตัวแปรฝั่งเซิร์ฟเวอร์เท่านั้นที่ไม่มีคำนำหน้า และอ่านมันภายในเส้นทางฝั่งเซิร์ฟเวอร์

การจำกัดคีย์ Gemini API ให้ใช้กับเว็บไซต์ของฉันเท่านั้นทำให้มันปลอดภัยในเบราว์เซอร์หรือไม่? มันช่วยได้ แต่สคริปต์ที่อยู่นอกเบราว์เซอร์สามารถส่งเฮดเดอร์ Referer อะไรก็ได้ คู่มือของ Google เองก็แนะนำให้ใช้พร็อกซีแบ็กเอนด์สำหรับแอปฝั่งไคลเอนต์

ฉันพบว่าคีย์ของฉันถูกเปิดเผย ควรทำอะไรก่อน? สร้างคีย์ใหม่ ดีพลอยมันฝั่งเซิร์ฟเวอร์ แล้วเพิกถอนคีย์เดิม ต่อไปให้ย้ายการเรียกไปไว้หลังเส้นทางที่ต้องยืนยันตัวตนพร้อมขีดจำกัดอัตราต่อผู้ใช้ และตั้งขีดจำกัดการใช้จ่ายของผู้ให้บริการ

เอเจนต์เขียนโค้ดของฉันสามารถแก้ไขและทดสอบสิ่งนี้ให้ฉันได้ไหม? ได้ ให้สั่งมันย้ายการเรียกผู้ให้บริการเข้าไปในเส้นทางฝั่งเซิร์ฟเวอร์ จากนั้นใช้ Apidog CLI สร้างและรันการทดสอบที่ยืนยันว่าได้ 401 เมื่อไม่มีเซสชัน ไม่มีรูปแบบคีย์ในคำตอบ และได้ 429 เมื่อเกินขีดจำกัดอัตราของคุณ

來源:DEV Community · dev.to