هل مفتاح API مكشوف في المتصفح؟ تحقق من مفتاح OpenAI أو Claude أو Gemini في دقيقتين
هل مفتاح API مكشوف في المتصفح؟ تحقق من مفتاح OpenAI أو Claude أو Gemini في دقيقتين إذا كان تطبيق الويب الخاص بك يستدعي OpenAI أو Claude أو Gemini مباشرةً من المتصفح، فإن مفتاح API الخاص بك مكشوف في المتصفح، وأي شخص يفت
هل مفتاح API مكشوف في المتصفح؟ تحقق من مفتاح OpenAI أو Claude أو Gemini في دقيقتين
إذا كان تطبيق الويب الخاص بك يستدعي OpenAI أو Claude أو Gemini مباشرةً من المتصفح، فإن مفتاح API الخاص بك مكشوف في المتصفح، وأي شخص يفتح DevTools يمكنه نسخه. يمكنك التأكد من ذلك في دقيقتين: افتح تبويب Network، أرسل طلب ذكاء اصطناعي واحد، وابحث عن ترويسة Authorization أو x-api-key أو x-goog-api-key المتجهة إلى مزوّد الخدمة. إذا رأيت مفتاحك هناك، فاستبدله اليوم وانقل الاستدعاء خلف مسار على الخادم.
فيما يلي: الفحص، وترتيب الإصلاح، وكيف يمكن لوكيل البرمجة الخاص بك اختبار المسار المُصلَّح.
لماذا يُعدّ مفتاح API المكشوف في المتصفح مشكلة
مفتاح API المكشوف في المتصفح هو سرّ يُشحن داخل كود الواجهة الأمامية أو حركة الشبكة، حيث يمكن لكل زائر قراءته. وإذا أرسل المتصفح المفتاح، يمكن للمستخدم نسخه واستدعاء مزوّد الخدمة على حسابك.
يقول دليل مفتاح Gemini API من Google: «لا تُضمّن مفاتيح API مباشرةً في تطبيقات الويب أو الجوال» لأن «المفاتيح المُصرَّفة في كود جهة العميل يمكن للمستخدمين استخراجها»، وينصحك بأن «تشغّل خادمًا وسيطًا (proxy) في الخلفية لإجراء استدعاءات API الفعلية». ويقدّم دليل أمان مفتاح API من OpenAI القاعدة نفسها: مرّر الطلبات عبر خادمك الخلفي بدلًا من نشر المفتاح إلى جهة العميل.
يقول ملف README الخاص بـ openai-node إن دعم المتصفح معطّل افتراضيًا «لتجنّب كشف بيانات اعتماد API السرية الخاصة بك»، وعليك ضبط dangerouslyAllowBrowser: true لتفعيله. ويستخدم TypeScript SDK الخاص بـ Anthropic اسم الخيار نفسه، ولا يقبل API الخاص به استدعاءات المتصفح (CORS) إلا عندما يحمل الطلب الترويسة anthropic-dangerous-direct-browser-access: true، كما وثّق Simon Willison عند إطلاقها.
هذا النمط شائع في التطبيقات المبنية بسرعة باستخدام أدوات بناء تطبيقات الذكاء الاصطناعي ووكلاء البرمجة: فأقصر مسار يعمل هو استدعاء fetch من مكوّن React مع المفتاح في متغيّر VITE_ أو NEXT_PUBLIC_. ثم يُعاد بيع المفاتيح المسروقة للاستدلال بالجملة، كما ورد في منشورنا حول مفاتيح AI API المسروقة وLLMjacking، وتكون فاتورتك هي المكان الذي تلاحظ فيه ذلك.
كيف تتحقق مما إذا كان مفتاح API الخاص بك مكشوفًا (دقيقتان)
نفّذ هذه الفحوصات على بيئة الإنتاج، لا على localhost.
-
راقب تبويب Network (45 ثانية). افتح موقعك، اضغط F12 (أو Cmd+Option+I على جهاز Mac)، اختر تبويب Network وشغّل ميزة ذكاء اصطناعي واحدة، مثل إرسال رسالة دردشة. صفِّ النتائج حسب
openaiأوanthropicأوgoogleapis. إذا ذهب أي طلب مباشرةً إلىapi.openai.comأوapi.anthropic.comأوgenerativelanguage.googleapis.com، فانقر عليه وافتح Headers. وجود مفتاح في[REDACTED CREDENTIAL] أوx-api-keyأوx-goog-api-keyأو وسيط استعلام?key=` يعني أنه مكشوف. -
ابحث في الحزمة (45 ثانية). في DevTools، اضغط Cmd+Option+F (أو Ctrl+Shift+F على Windows) للبحث في جميع السكربتات المُحمَّلة. ابحث عن
sk-وsk-ant-وAIzaوdangerouslyAllowBrowser. أي إصابة في ملف.jsيُقدَّم إلى المتصفح هي تسريب، حتى لو لم يُطلق أي طلب بعد. -
تحقق من بادئات متغيّرات البيئة (30 ثانية). افتح ملف
.envولوحة تحكم الاستضافة لديك. تنسخ أدوات البناء بعض المتغيّرات إلى حزمة العميل بحكم تصميمها:VITE_*في Vite (انظر وثائق بيئة 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. انقل الاستدعاء إلى الخادم | ضع استدعاء المزوّد في مسار خادم أو دالة حافة (edge function) تحتفظ بالمفتاح | يمنع المفتاح الجديد من التسريب بالطريقة نفسها | 30 إلى 60 دقيقة |
| 3. أضف المصادقة وحدود المعدل لكل مستخدم | اشترط وجود جلسة على المسار وحدّد سقف الطلبات لكل مستخدم | بدون ذلك يصبح الوسيط الخاص بك نقطة نهاية ذكاء اصطناعي مجانية لأي شخص | 30 دقيقة |
| 4. اضبط حدود الإنفاق لدى المزوّد | اضبط الميزانيات أو حدود الإنفاق في إعدادات الفوترة لكل مزوّد | يحدّ من أسوأ فاتورة محتملة إذا تسرّب شيء آخر | 10 دقائق |
| 5. افحص في CI | أفشل البناء إذا ظهر نمط مفتاح في حزمة المخرجات | يمنع الميزة التالية التي يكتبها الوكيل من إلغاء الإصلاح | 15 دقيقة |
انشر المفتاح الجديد على الخادم قبل أن تبطل المفتاح القديم، وإلا فسيتوقف عمل ميزة الذكاء الاصطناعي لديك. يغطي دليلنا حول أفضل ممارسات استبدال مفاتيح API الاستبدال دون أي توقف. وفي الخطوة 4، توصي إرشادات أمان المفاتيح من OpenAI بحدود إنفاق مع تطبيق صارم، لأن التنبيهات وحدها لا توقف الحركة؛ وميزانية الفوترة في Google Cloud تنبّهك لكنها لا توقف الاستخدام بحد ذاتها.
كيف تنقل استدعاءات OpenAI وClaude وGemini خلف مسار على الخادم
مسار الوسيط الخلفي هو نقطة نهاية خادم صغيرة تستقبل طلب المستخدم، وتضيف مفتاح 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، لذا تعامل معها كطبقة ثانية. والحالة الوحيدة المعقولة للاستدعاء من المتصفح هي أداة «أحضِر مفتاحك الخاص» حيث يلصق كل مستخدم مفتاحه الخاص.
دع وكيل البرمجة الخاص بك يختبر مسار الوسيط باستخدام 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:
- a request without a session cookie returns 401,
- a valid request returns 200 and the body does not contain "sk-", "sk-ant-", "AIza" or "api_key",
- 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 باستخدام ماسح أسرار. وإذا كانت وكلاؤك يستدعون أدوات تتعامل مع بيانات العملاء، فمنشور اختبار وكلاء الذكاء الاصطناعي بحثًا عن تسريبات البيانات يغطي الطبقة التالية.
أخطاء شائعة تُبقي المفتاح مكشوفًا
- ترميز المفتاح بـ Base64 أو تقسيمه. يجب أن يعيد المتصفح بناءه لإرساله، لذا يُظهر DevTools القيمة الحقيقية.
- نقله إلى localStorage أو ملف تعريف ارتباط. لا يزال على جهة العميل.
-
الوساطة دون مصادقة. مسار
/api/chatعام هو نقطة نهاية ذكاء اصطناعي مفتوحة على فاتورتك. - الاحتفاظ بالمفتاح القديم «تحسّبًا». المفتاح الذي كان عامًّا ليوم واحد قد نُسخ.
- نسيان تطبيق الجوال. يذكر دليل Google «تطبيقات الويب أو الجوال» معًا لسبب وجيه.
الأسئلة الشائعة
كيف أعرف ما إذا كان مفتاح OpenAI API الخاص بي مكشوفًا في المتصفح؟ شغّل ميزة ذكاء اصطناعي مع فتح تبويب Network في DevTools. وجود طلب إلى api.openai.com مع ترويسة Authorization: Bearer [REDACTED]-... يعني أنه مكشوف. وابحث أيضًا في السكربتات المُحمَّلة عن sk- وdangerouslyAllowBrowser.
هل من الآمن وضع مفتاح API في متغيّر بيئة NEXT_PUBLIC_ أو VITE_؟ لا. هذه البادئات موجودة لنسخ القيم إلى حزمة العميل. أي مفتاح مزوّد مخزَّن تحتها يُشحن إلى كل زائر. استخدم متغيّرًا غير مُبدوء ببادئة مخصّصًا للخادم واقرأه داخل مسار على الخادم.
هل تقييد مفتاح Gemini API بموقعي يجعله آمنًا في المتصفح؟ يساعد، لكن يمكن لسكربت خارج المتصفح إرسال أي ترويسة Referer. ويوصي دليل Google نفسه باستخدام وسيط خلفي لتطبيقات جهة العميل.
وجدت مفتاحي مكشوفًا. ماذا أفعل أولًا؟ أنشئ مفتاحًا جديدًا، وانشره على الخادم، ثم أبطِل القديم. بعد ذلك، انقل الاستدعاء خلف مسار مُوثَّق بحدود معدل لكل مستخدم، واضبط حدود الإنفاق لدى المزوّد.
هل يمكن لوكيل البرمجة الخاص بي إصلاح هذا واختباره نيابةً عني؟ نعم. اطلب منه نقل استدعاء المزوّد إلى مسار على الخادم، ثم استخدم Apidog CLI لإنشاء اختبارات وتشغيلها تتحقق من إرجاع 401 دون جلسة، وعدم وجود أنماط مفاتيح في الاستجابات، وإرجاع 429 بعد تجاوز حد المعدل.
Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.
