Dev.to Security 🔐 Cybersecurity 👁 0 📖 15 min read

API Key bị lộ trên trình duyệt? Kiểm tra key OpenAI, Claude hoặc Gemini trong 2 phút

Nếu ứng dụng web của bạn gọi OpenAI, Claude hay Gemini trực tiếp từ trình duyệt, khóa API của bạn bị lộ trong trình duyệt, và bất kỳ ai mở DevTools đều có thể sao chép nó. Bạn có thể xác nhận điều này trong 2 phút: mở ta

API Key bị lộ trên trình duyệt? Kiểm tra key OpenAI, Claude hoặc Gemini trong 2 phút

Nếu ứng dụng web của bạn gọi OpenAI, Claude hay Gemini trực tiếp từ trình duyệt, khóa API của bạn bị lộ trong trình duyệt, và bất kỳ ai mở DevTools đều có thể sao chép nó. Bạn có thể xác nhận điều này trong 2 phút: mở tab Network, gửi một yêu cầu AI, và tìm header Authorization, x-api-key hoặc x-goog-api-key gửi đến nhà cung cấp. Nếu bạn thấy khóa của mình ở đó, hãy xoay vòng nó ngay hôm nay và chuyển lệnh gọi ra sau một route phía máy chủ.

Dùng thử Apidog ngay hôm nay

Dưới đây: cách kiểm tra, thứ tự khắc phục, và cách coding agent của bạn có thể kiểm thử route đã sửa.

Tại sao khóa API bị lộ trong trình duyệt lại là vấn đề

Một khóa API bị lộ trong trình duyệt là một bí mật được vận chuyển bên trong mã frontend hoặc lưu lượng mạng của bạn, nơi mọi khách truy cập đều có thể đọc được. Nếu trình duyệt gửi khóa, người dùng có thể sao chép nó và gọi nhà cung cấp tính vào hóa đơn của bạn.

Hướng dẫn về khóa API Gemini của Google nói rằng "Không hardcode khóa API trực tiếp trong ứng dụng web hoặc di động" vì "Khóa được biên dịch trong mã phía client có thể bị người dùng trích xuất," và khuyên bạn nên "chạy một máy chủ proxy backend để thực hiện các lệnh gọi API thực tế." Hướng dẫn an toàn khóa API của OpenAI cũng đưa ra quy tắc tương tự: định tuyến các yêu cầu qua backend của riêng bạn thay vì triển khai khóa đến client.

README của openai-node nói rằng hỗ trợ trình duyệt bị tắt theo mặc định "để tránh làm lộ thông tin xác thực API bí mật của bạn," và bạn phải đặt dangerouslyAllowBrowser: true để bật nó lên. SDK TypeScript của Anthropic dùng cùng tên tùy chọn, và API của nó chỉ chấp nhận các lệnh gọi trình duyệt (CORS) khi yêu cầu mang header anthropic-dangerous-direct-browser-access: true, như Simon Willison đã ghi lại khi nó ra mắt.

Mô hình này phổ biến trong các ứng dụng được xây dựng nhanh bằng AI app builder và coding agent: con đường hoạt động ngắn nhất là một fetch từ component React với khóa nằm trong biến VITE_ hoặc NEXT_PUBLIC_. Các khóa bị đánh cắp sau đó được bán lại để suy luận hàng loạt, như đã đề cập trong bài viết của chúng tôi về khóa AI API bị đánh cắp và LLMjacking, và hóa đơn của bạn là nơi bạn nhận ra điều đó.

Cách kiểm tra khóa API của bạn có bị lộ không (2 phút)

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_

Hãy chạy các kiểm tra này trên môi trường production, không phải localhost.

  1. Quan sát tab Network (45 giây). Mở trang web của bạn, nhấn F12 (Cmd+Option+I trên Mac), chọn tab Network và kích hoạt một tính năng AI, chẳng hạn gửi một tin nhắn chat. Lọc theo openai, anthropic hoặc googleapis. Nếu có bất kỳ yêu cầu nào đi thẳng đến api.openai.com, api.anthropic.com hoặc generativelanguage.googleapis.com, hãy nhấp vào nó và mở Headers. Một khóa trong [REDACTED CREDENTIAL],x-api-key,x-goog-api-keyhoặc tham số truy vấn?key=` nghĩa là nó đã bị lộ.
  2. Tìm kiếm trong bundle (45 giây). Trong DevTools, nhấn Cmd+Option+F (Ctrl+Shift+F trên Windows) để tìm kiếm tất cả các script đã tải. Tìm sk-, sk-ant-, AIza và dangerouslyAllowBrowser. Bất kỳ kết quả nào trong tệp .js được phục vụ cho trình duyệt đều là rò rỉ, ngay cả khi chưa có yêu cầu nào được gửi đi.
  3. Kiểm tra tiền tố biến môi trường của bạn (30 giây). Mở tệp .env và bảng điều khiển hosting của bạn. Các công cụ build sao chép một số biến vào bundle client theo thiết kế: VITE_* trong Vite (xem tài liệu về env của Vite), NEXT_PUBLIC_* trong Next.js, REACT_APP_* trong Create React App và EXPO_PUBLIC_* trong Expo. Một khóa nhà cung cấp nằm sau bất kỳ tiền tố nào trong số này đều là công khai, bất kể nó được đặt tên như thế nào.

Để quét đầu ra build bằng terminal:

`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
`

Không có kết quả nghĩa là đạt. Google cho biết các khóa AI Studio mới được tạo từ ngày 28 tháng 5 năm 2026 là "auth key," có định dạng có thể khác với AIza, vì vậy hãy tìm cả generativelanguage.googleapis.com trong bundle. Bạn chỉ đạt khi trình duyệt chỉ giao tiếp với tên miền của riêng bạn (ví dụ /api/chat) và không có hostname nhà cung cấp hay khóa nào xuất hiện ở bất kỳ đâu.

Phải làm gì khi khóa của bạn bị lộ: thứ tự khắc phục

Xoay vòng trước sẽ đóng lỗ hổng của ngày hôm nay; thay đổi kiến trúc sẽ đóng nó vĩnh viễn.

Bước Bạn làm gì Tại sao theo thứ tự này Thời gian
1. Xoay vòng khóa Tạo khóa mới trong bảng điều khiển nhà cung cấp, sau đó thu hồi khóa bị lộ Khóa cũ có thể đã bị sao chép; thu hồi là cách duy nhất để ngăn việc sử dụng nó 5 phút
2. Chuyển lệnh gọi sang phía máy chủ Đặt lệnh gọi nhà cung cấp trong một route máy chủ hoặc edge function giữ khóa Ngăn khóa mới bị lộ theo cùng cách 30 đến 60 phút
3. Thêm xác thực và giới hạn tốc độ theo người dùng Yêu cầu phiên đăng nhập trên route và giới hạn số yêu cầu mỗi người dùng Nếu không có điều này, proxy của bạn trở thành endpoint AI miễn phí cho bất kỳ ai 30 phút
4. Đặt giới hạn chi tiêu của nhà cung cấp Đặt ngân sách hoặc giới hạn chi tiêu trong cài đặt thanh toán của mỗi nhà cung cấp Giới hạn hóa đơn trong trường hợp xấu nhất nếu có thứ khác bị lộ 10 phút
5. Quét trong CI Làm build thất bại nếu một mẫu khóa xuất hiện trong bundle đầu ra Ngăn tính năng tiếp theo do agent viết hoàn tác bản sửa 15 phút

Triển khai khóa mới ở phía máy chủ trước khi bạn thu hồi khóa cũ, nếu không tính năng AI của bạn sẽ ngừng hoạt động. Hướng dẫn của chúng tôi về thực hành tốt nhất khi xoay vòng khóa API đề cập đến việc xoay vòng không gián đoạn. Ở bước 4, hướng dẫn an toàn khóa của OpenAI khuyến nghị giới hạn chi tiêu với thực thi cứng, vì cảnh báo đơn thuần không ngăn được lưu lượng; ngân sách thanh toán của Google Cloud chỉ cảnh báo bạn chứ tự nó không ngăn việc sử dụng.

Cách chuyển các lệnh gọi OpenAI, Claude và Gemini ra sau một route máy chủ

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

Một route proxy backend là một endpoint máy chủ nhỏ nhận prompt của người dùng, thêm khóa API ở phía máy chủ, gọi nhà cung cấp và chỉ trả về câu trả lời. Trình duyệt không bao giờ thấy khóa. Đây là một route Next.js tối giản cho OpenAI; Claude và Gemini cũng theo hình mẫu tương tự:

`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
}
`

Điều làm cho nó an toàn, chứ không chỉ bị ẩn:

  • Model nằm trên máy chủ. Nếu trình duyệt chọn model, kẻ tấn công sẽ chọn cái đắt nhất.
  • Đầu vào được giới hạn. Không có prompt khổng lồ đi qua tài khoản của bạn.
  • Chỉ câu trả lời được trả về. Không bao giờ trả về phản hồi thô của nhà cung cấp hay đối tượng lỗi.
  • Không có tiền tố công khai. OPENAI_API_KEY ở lại phía máy chủ; NEXT_PUBLIC_OPENAI_API_KEY sẽ được gửi đến mọi khách truy cập.

Đối với Claude và Gemini, hãy dùng SDK của chúng với ANTHROPIC_API_KEY hoặc GEMINI_API_KEY. Các hạn chế về website và IP của Google có ích, nhưng bất kỳ ai với curl đều có thể gửi bất kỳ header Referer nào, vì vậy hãy coi chúng là một lớp bảo vệ thứ hai. Trường hợp hợp lý duy nhất cho lệnh gọi từ trình duyệt là công cụ mang khóa riêng (bring-your-own-key) nơi mỗi người dùng tự dán khóa của họ.

Để coding agent của bạn kiểm thử route proxy bằng Apidog CLI

Di chuyển khóa chỉ là một nửa công việc; nửa còn lại là chứng minh nó vẫn được giữ nguyên sau tính năng tiếp theo mà agent của bạn viết. Hãy để agent đã xây dựng route kiểm thử nó bằng Apidog CLI. Thiết lập một lần:

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

Sau đó đưa cho Claude Code, Codex, Cursor hoặc bất kỳ agent nào bạn dùng một [REDACTED PROMPT]
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. `shell

Agent đọc schema, xác thực payload của nó, viết các test case và chạy chúng:

`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
`

Khẳng định thứ hai là cái quan trọng nhất: nó thất bại ngay khi một đối tượng lỗi nhà cung cấp hoặc trường gỡ lỗi bắt đầu phản hồi khóa của bạn về phía client. Hãy đặt cùng bước apidog run ... -r junit vào pipeline của bạn để mỗi lần triển khai đều kiểm tra lại nó; hướng dẫn Apidog CLI CI/CD của chúng tôi trình bày cách thiết lập, và Apidog CLI trong Claude Code đề cập đến phía agent. Gói Miễn phí của Apidog hỗ trợ tối đa 4 người dùng, nên một nhóm nhỏ có thể chạy điều này mà không cần ghế trả phí.

Về phía repo của cùng vấn đề, các khóa được commit vào Git thay vì gửi đến trình duyệt, hãy xem bài viết của chúng tôi về kiểm tra rò rỉ khóa API bằng secret scanner. Nếu agent của bạn gọi các công cụ xử lý dữ liệu khách hàng, kiểm thử AI agent về rò rỉ dữ liệu đề cập đến lớp tiếp theo.

Những sai lầm phổ biến khiến khóa vẫn bị lộ

  • Mã hóa Base64 hoặc chia nhỏ khóa. Trình duyệt phải dựng lại nó để gửi đi, nên DevTools sẽ hiển thị giá trị thật.
  • Chuyển nó vào localStorage hoặc cookie. Nó vẫn ở trên client.
  • Proxy mà không có xác thực. Một /api/chat công khai là endpoint AI mở tính vào hóa đơn của bạn.
  • Giữ khóa cũ "để phòng hờ." Một khóa đã công khai trong một ngày là đã bị sao chép.
  • Quên ứng dụng di động. Hướng dẫn của Google nhắc đến "ứng dụng web hoặc di động" cùng nhau là có lý do.

FAQ

Làm sao tôi biết khóa API OpenAI của mình có bị lộ trong trình duyệt không? Kích hoạt một tính năng AI với tab Network của DevTools đang mở. Một yêu cầu đến api.openai.com với header Authorization: Bearer [REDACTED]-... nghĩa là nó đã bị lộ. Đồng thời tìm sk- và dangerouslyAllowBrowser trong các script đã tải.

Có an toàn không khi đặt khóa API trong biến môi trường NEXT_PUBLIC_ hoặc VITE_? Không. Những tiền tố đó tồn tại để sao chép giá trị vào bundle client. Bất kỳ khóa nhà cung cấp nào được lưu dưới chúng đều được gửi đến mọi khách truy cập. Hãy dùng biến chỉ dành cho máy chủ không có tiền tố và đọc nó bên trong một route máy chủ.

Việc giới hạn khóa API Gemini cho website của tôi có làm nó an toàn trong trình duyệt không? Nó có ích, nhưng một script bên ngoài trình duyệt có thể gửi bất kỳ header Referer nào. Chính hướng dẫn của Google khuyến nghị dùng proxy backend cho các ứng dụng client.

Tôi phát hiện khóa của mình bị lộ. Tôi nên làm gì trước? Tạo khóa mới, triển khai nó ở phía máy chủ, sau đó thu hồi khóa cũ. Tiếp theo, chuyển lệnh gọi ra sau một route có xác thực với giới hạn tốc độ theo người dùng, và đặt giới hạn chi tiêu của nhà cung cấp.

Coding agent của tôi có thể sửa và kiểm thử điều này cho tôi không? Có. Yêu cầu nó chuyển lệnh gọi nhà cung cấp vào một route máy chủ, sau đó dùng Apidog CLI để tạo và chạy các kiểm thử khẳng định trả về 401 khi không có phiên, không có mẫu khóa trong phản hồi, và 429 khi vượt giới hạn tốc độ của bạn.

📰 Read the original article on Dev.to Security

Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.