كيف تُخفي مفتاح API في JavaScript الخاص بالواجهة الأمامية دون تسريبه

الكود الذي يعمل في المتصفح عام بطبيعته، لذلك يمكن نسخ أي مفتاح API تضعه في حزمة React أو Vue أو JavaScript عادية خلال ثوانٍ. يوضح هذا المقال كيف يحافظ وكيل خادم صغير وحدود الاستخدام والتوكنات المقيّدة والتدوير السريع للمفاتيح على بياناتك الاعتمادية بعيدًا عن متناول الآخرين.

كيف تُخفي مفتاح API في JavaScript الخاص بالواجهة الأمامية دون تسريبه
Cristian Da Conceicao
مؤسس Picasso IA

افتح أي موقع بنيته الشهر الماضي، واضغط F12، ثم انقر على تبويب Network. ستجد هناك كل ترويسة أرسلها JavaScript الخاص بك بنص واضح، بما في ذلك قيمة Authorization التي كنت متأكدًا أن أحدًا لن يجدها. هذه هي الحقيقة المزعجة وراء كيف تُخفي مفتاح API في JavaScript الخاص بالواجهة الأمامية: لا يمكنك إخفاؤه هناك. ما يمكنك فعله هو التوقف عن إرسال السر إلى المتصفح أصلًا، والسماح لخادم تتحكم فيه بإجراء الطلب نيابةً عن مستخدميك.

يشرح هذا المقال بالتفصيل كيف يعمل ذلك. سترى لماذا تُسرّب أدوات التجميع متغيرات البيئة، وكيف تعثر الروبوتات على التوكنات خلال دقائق من النشر، وكيف تبني وكيلًا صغيرًا باستخدام Express أو على Cloudflare Workers يحتفظ ببيانات اعتمادك على الخادم. بعد ذلك نضيف حدود الاستخدام، والتحقق من المدخلات، والتوكنات المقيّدة، وخطة استجابة واضحة لليوم الذي يتسرب فيه المفتاح رغم كل شيء.

💡 الإجابة المختصرة: إذا شُحن سرّ داخل كود يعمل في المتصفح، فهو عام. أخفِه بنقل الطلب إلى الواجهة الخلفية، لا بترميز السلسلة أو تقسيمها أو تشويشها.

لماذا لا يستطيع كود الواجهة الأمامية حفظ الأسرار

مطوّر يفحص تبويب الشبكة في المتصفح على شاشة كبيرة داخل مكتب مضيء

يعمل المتصفح بتنزيل الكود وتشغيله على جهاز الزائر. كل ما يُنزَّل يستطيع الزائر قراءته: HTML وCSS وحزم JavaScript وخرائط المصدر وكل طلب يرسله كودك. لا يوجد إعداد أو علامة أو خطوة بناء تجعل السلسلة غير مرئية للشخص الذي يشغّل الجهاز.

كل شيء قابل للقراءة في المتصفح

ثلاثة أماكن تكشف التوكن دون أي مهارة في الاختراق:

  • تبويب Network. كل طلب يعرض عنوانه وترويساته وحمولته. توكن Bearer في ترويسة لا يبعد عنك إلا بنقرة واحدة.
  • تبويب Sources. حزمتك موجودة هناك، وإذا فُعّلت خرائط المصدر فستظهر ملفاتك الأصلية أيضًا مع تعليقاتها.
  • View source وcurl. يستطيع أي شخص تنزيل حزمتك وتشغيل grep للبحث عن بادئات التوكنات مثل sk_ أو pia_sk_.

أدوات التجميع تضمّن متغيراتك داخل الكود

من الأخطاء الشائعة أن يُقال: "وضعته في ملف .env، إذن هو خاص." ملف .env خاص فعلًا. أما ما تفعله أداة التجميع به فقضية أخرى. تستبدل Vite وNext.js وCreate React App المتغيرات ذات البادئات الخاصة بقيمها الحرفية وقت البناء.

الإطارالبادئة التي تصبح عامةما الذي يحدث
ViteVITE_تُضمَّن القيمة داخل الحزمة
Next.jsNEXT_PUBLIC_تُضمَّن القيمة داخل كود العميل
Create React AppREACT_APP_تُضمَّن القيمة وقت البناء
NuxtNUXT_PUBLIC_تنتقل القيمة إلى الإعدادات العامة لوقت التشغيل

إذن فإن VITE_PROVIDER_TOKEN=abc123 داخل ملف .env يصبح في النهاية النص الحرفي "abc123" داخل assets/index-xxxx.js. أما المتغيرات التي لا تحمل البادئة العامة فتبقى خارج حزمة العميل، وهذا بالضبط ما يجعل السر مكانه على الخادم.

التشويش لا يفعل سوى إبطاء عملك

Base64 وتقسيم السلاسل وعكس الحروف وحيل XOR: لا شيء من ذلك يفيد، لأن كودك يجب أن يعيد بناء القيمة الحقيقية قبل إرسال الطلب. وبمجرد أن يغادر الطلب، يعرض تبويب Network النتيجة النهائية. التشويش يمنح المهاجم عشر دقائق من الإزعاج، ويمنحك صداعًا دائمًا في الصيانة.

كيف تحدث التسريبات فعلًا

الروبوتات تفحص المستودعات والحزم

أسرع طريقة هي أيضًا الأكثر مللًا: افتح الصفحة، وشغّل الميزة، واقرأ الترويسة. لا حاجة لأي سكربت. الماسحات الآلية تذهب أبعد من ذلك، إذ تزحف عبر المستودعات العامة وحزم npm والمواقع الحية بحثًا عن صيغ التوكنات المعروفة، وكثير من المزودين يستخدمون بادئات مميزة (sk_، ghp_، pia_sk_) تحديدًا كي تتمكن الماسحات من التعرف عليها.

يمكن أن يُلتقط توكن رُفع إلى مستودع GitHub عام خلال دقائق. بعض المزودين يفحصون ويلغون التوكنات تلقائيًا، وهذه ميزة جيدة، لكنها ليست خطة.

ما الذي يكلفك التسريب فعلًا

مطوّر قلق يضع يده على جبينه في وقت متأخر من المساء أمام حاسوب محمول

في واجهات برمجة التطبيقات التي تُدفع حسب الاستخدام، تكون الفاتورة هي الضرر المرئي. أما الضرر الخفي فأسوأ: حصص مستنفدة تُسقط تطبيقك نفسه، وإساءة استخدام تُسجَّل على حسابك، وإذا كانت الصلاحيات واسعة، فيصبح الوصول إلى بيانات حقيقية ممكنًا.

بيانات الاعتماد المسرّبةالإساءة المعتادةما الذي يكلفك
توكن نموذج لغوي كبيردردشات مجانية، توليد رسائل مزعجةفاتورة التوكنات، وتوقف بسبب حدود الاستخدام
توكن توليد الصور أو الفيديوتصيير بالجملة، إعادة بيعفاتورة استخدام GPU
توكن الخرائط أو البحثكشط بيانات على نطاق واسعاستنفاد الحصة
سر قاعدة بيانات أو تخزينقراءة السجلات أو حذفهاتسريب بيانات

تطبيقات الذكاء الاصطناعي هي الهدف المفضل. نماذج مثل GPT 5.6 Luna أو Claude Sonnet 5 تحاسب بحسب التوكنات، فتتحول بيانات الاعتماد المسروقة مباشرة إلى حوسبة مجانية لغيرك على حسابك.

ضع وكيلًا بين المتصفح وواجهة البرمجة

منظر علوي ليد ترسم مخطط معماري من ثلاثة صناديق في دفتر ملاحظات

الحل معماري في جوهره. بدلًا من أن يستدعي المتصفح المزوّد مباشرة، يستدعي خادمك، ثم يستدعي خادمك المزوّد.

Browser  ->  POST /api/generate  ->  Your server  ->  Provider API
                                      (holds the secret)

ثلاث قواعد تحافظ على سلامة هذا التصميم:

  1. يعيش السر فقط في متغيرات بيئة الخادم. ليس في المستودع أبدًا، ولا في متغير من نوع NEXT_PUBLIC_.
  2. يرسل المتصفح مدخلات المستخدم فقط. نص أمر، أو معرّف، أو خيار من قائمة. لا يرسل عنوانًا أو ترويسة أو اسم نموذج يختاره بحرية.
  3. يقرر الخادم ما هو مسموح. يتحقق من المدخلات، ويضيف بيانات الاعتماد، ويعيد توجيه الاستدعاء، ويعيد الحقول التي تحتاجها الصفحة فقط.

وكيل Express يعمل

يوجّه هذا المثال طلب صورة إلى واجهة PicassoIA API، التي تتحقق من الهوية بتوكن Bearer في ترويسة Authorization، وتعرض التنبؤات على /v1/models/{owner}/{name}/predictions. يصلح النمط نفسه مع أي مزود آخر.

// server.js
import express from "express";
import rateLimit from "express-rate-limit";

const app = express();
app.use(express.json({ limit: "20kb" }));
app.use("/api/", rateLimit({ windowMs: 60_000, limit: 10 }));

const UPSTREAM =
  "https://api.picassoia.com/v1/models/picassoia/picassoia-image/predictions";

app.post("/api/generate", async (req, res) => {
  const { prompt } = req.body ?? {};

  if (typeof prompt !== "string" || prompt.length === 0 || prompt.length > 500) {
    return res.status(400).json({ error: "Prompt must be 1 to 500 characters." });
  }

  try {
    const upstream = await fetch(UPSTREAM, {
      method: "POST",
      headers: {
        Authorization: `Bearer ${process.env.PICASSOIA_TOKEN}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ input: { prompt } }),
    });

    const data = await upstream.json();
    // Return only what the browser needs, never the raw upstream body.
    res.status(upstream.status).json({ id: data.id, status: data.status });
  } catch {
    res.status(502).json({ error: "Upstream request failed." });
  }
});

app.listen(3000);

شغّله بالأمر node --env-file=.env server.js (Node 20.6 أو أحدث) حتى يأتي التوكن من ملف غير مُتتبَّع في Git. واجهة PicassoIA API غير متزامنة: تنشئ تنبؤًا ثم تستعلم عن نتيجته. أضف مسار GET /api/result/:id ثانيًا مبنيًا بالطريقة نفسها، وتحقق من حقول الاستجابة الدقيقة في صفحة PicassoIA API.

نسخة بلا خادم على Cloudflare Workers

لقطة واسعة من زاوية منخفضة لممر بين رفوف خوادم في مركز بيانات

لا تريد صيانة خادم؟ يؤدي Worker المهمة نفسها بأسطر أقل. خزّن السر باستخدام npx wrangler secret put PICASSOIA_TOKEN، فلن يمسّ مستودعك أبدًا.

// worker.js
const UPSTREAM =
  "https://api.picassoia.com/v1/models/picassoia/picassoia-image/predictions";

export default {
  async fetch(request, env) {
    const url = new URL(request.url);

    if (request.method !== "POST" || url.pathname !== "/api/generate") {
      return new Response("Not found", { status: 404 });
    }

    const { prompt } = await request.json().catch(() => ({}));
    if (typeof prompt !== "string" || prompt.length === 0 || prompt.length > 500) {
      return new Response("Bad request", { status: 400 });
    }

    const upstream = await fetch(UPSTREAM, {
      method: "POST",
      headers: {
        Authorization: `Bearer ${env.PICASSOIA_TOKEN}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ input: { prompt } }),
    });

    const data = await upstream.json();
    return Response.json(
      { id: data.id, status: data.status },
      { status: upstream.status }
    );
  },
};

تتبع Vercel Functions وNetlify Functions وAWS Lambda النمط نفسه: مسار صغير واحد، وسر واحد في إعدادات المنصة، ولا بيانات اعتماد في كود العميل.

كود الواجهة الأمامية بعد الإصلاح

يتحدث المتصفح الآن إلى مسارك الخاص فقط:

async function generate(prompt) {
  const res = await fetch("/api/generate", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ prompt }),
  });

  if (!res.ok) throw new Error(`Request failed: ${res.status}`);
  return res.json();
}

ابنِ التطبيق، وافتح الحزمة وابحث فيها. لا ينبغي أن يبقى شيء لتجده.

أحكم إغلاق الوكيل أيضًا

وكيل بلا حدود ليس إلا طريقة أكثر راحة للغرباء كي ينفقوا مالك. تعامل معه كنقطة نهاية عامة، لأنها كذلك فعلًا.

💡 عن CORS: ضبط Access-Control-Allow-Origin على نطاقك الخاص يمنع المواقع الأخرى من استدعاء وكيلك من متصفح الزائر. لكنه لا يفعل شيئًا ضد curl أو السكربتات. الحماية الحقيقية تأتي من المصادقة وحدود الاستخدام والتحقق من المدخلات.

حدود الاستخدام لكل مستخدم أو عنوان IP

بوابة دوّارة من الفولاذ المقاوم للصدأ في ردهة مكتب حديثة، يعبرها زائر واحد

قيّد الاستخدام حسب المستخدم المُصادَق عليه عندما تكون لديك تسجيلات دخول، وحسب عنوان IP عندما لا تكون لديك. خلف CDN أو موازن أحمال، تأكد من أن إطار العمل لديك يقرأ عنوان IP الحقيقي للعميل (في Express، يعني ذلك ضبط trust proxy بشكل صحيح)، وإلا فسيشترك كل زائر في الحصة نفسها.

نوع نقطة النهايةالحد المبدئيالسبب
توليد النصوص20 طلبًا في الدقيقة لكل مستخدمتكلفة الطلب منخفضة، ويسهل إساءة استخدامها بالطلبات المتكررة
توليد الصور5 طلبات في الدقيقة لكل مستخدمتكلفة GPU حقيقية في كل طلب
توليد الفيديو2 طلبان في الدقيقة لكل مستخدمبطيء ومكلف
فحوصات الحالة للقراءة فقط60 طلبًا في الدقيقة لكل مستخدمالاستعلام المتكرر أمر طبيعي

تعامل مع هذه الأرقام كنقاط بداية، واضبطها وفق حركة المرور الفعلية.

تحقق من كل مدخل

لا تُمرّر جسم طلب المتصفح كما يصلك أبدًا. تحقق من كل حقل على حدة:

  • حدّد طول أمر النص. المثال أعلاه يرفض أي نص يتجاوز 500 حرف.
  • اسمح بقائمة محددة من النماذج. دع الصفحة ترسل "fast" أو "quality"، ثم اربط هذه التسميات بأسماء النماذج الفعلية على الخادم.
  • حدّد حجم المخرجات. اضبط حدًا أقصى لعدد التوكنات أو الصور في كل استدعاء.
  • ارفض الحقول غير المعروفة. إذا كان المخطط يقول prompt، فلا يمر أي شيء آخر.

ضع سقوف ميزانية لدى المزوّد

تتيح لك معظم المزودات ضبط حدود إنفاق شهرية وتنبيهات. فعّلها. هذه شبكة الأمان ليوم تفشل فيه كل الطبقات الأخرى. واستخدم أيضًا توكنًا منفصلًا لكل مشروع ولكل بيئة، حتى لا يؤدي إلغاء واحد إلى تعطيل البقية.

متى يكون التوكن العام مقبولًا

ليست كل بيانات الاعتماد سرية. بعضها مصمم للمتصفحات: إعدادات Firebase للويب، وتوكنات Stripe القابلة للنشر (تلك التي تبدأ بالبادئة pk_) وتوكنات Google Maps JavaScript. هي آمنة فقط عندما تقيّدها.

قيّد حسب النطاق والصلاحية

لقطة مقربة لليد تمسك حلقة معدنية قديمة مصنوعة من قطع نحاسية

افتح لوحة تحكم المزود وطبّق كل قيد متاح:

  • قيود المُحيل HTTP بحيث يعمل التوكن فقط من yourdomain.com.
  • قيود نطاق API بحيث يستدعي توكن الخرائط الخرائط ولا شيئًا آخر.
  • حصص يومية بحيث يصطدم أي اندفاع للإساءة بسقف.

تحذير واحد: فحوصات المُحيل تعتمد على ترويسة Referer، ويمكن للعملاء غير المتصفحين تزييفها. القيود تقلل الإساءة العرضية، لكنها لا تحوّل التوكن العام إلى سر. أبقِ القيمة محدودة النطاق، ومحددة الميزانية.

توكنات قصيرة العمر للمتصفحات

مهندس يرسم مخططًا من ثلاث خطوات بأسهم على لوح زجاجي في غرفة اجتماعات

بعض المهام أثقل من أن تُمرَّر عبر خادمك، مثل الرفع الكبير أو البث الفوري. لهذه المهام استخدم تبادل التوكنات:

  1. يسجّل المستخدم الدخول إلى خلفيتك أنت.
  2. تطلب خلفيتك من المزود توكنًا قصير العمر ومحدود الصلاحية، أو توقّع JWT تنتهي صلاحيته خلال 5 إلى 15 دقيقة.
  3. يستخدم المتصفح هذا التوكن المؤقت مباشرة للطلب الثقيل.
  4. ينتهي التوكن من تلقاء نفسه، فتصبح القيمة المنسوخة عديمة الفائدة بعد وقت قصير.

تقدم عدة منصات للبث الفوري والتخزين توكنات مؤقتة لهذا النمط تحديدًا. ويبقى سرك طويل العمر على الخادم دائمًا.

ماذا تفعل بعد التسريب

ألغِ أولًا، ثم حقّق

مطوّران يعملان جنبًا إلى جنب على طاولة مشتركة وبينهما صندوق فولاذي مغلق

إذا بقي توكن عامًا ولو لساعة واحدة، فافترض أن أحدًا نسخه. اعمل وفق هذه القائمة بالترتيب:

  1. ألغِ التوكن أو دوّره من لوحة تحكم المزود فورًا.
  2. انشر البديل في بيئة خادمك فقط.
  3. اقرأ سجلات الاستخدام لفترة التعرض، وابحث عن عناوين IP أو نماذج أو ارتفاعات غير مألوفة.
  4. شدّد حدود الإنفاق قبل أي شيء آخر.
  5. أخبر فريقك، وتحقق مما إذا أُعيد استخدام القيمة نفسها في مكان آخر.

نظّف سجل Git والحزم

حذف السر من آخر commit لا يصلح شيئًا، لأن السجل ما زال يحتفظ به. وقد تحتفظ به النشرات القديمة وذاكرات CDN وخرائط المصدر العامة أيضًا. التدوير هو الإصلاح الحقيقي. أما إعادة كتابة السجل باستخدام git filter-repo فهي نظافة تأتي بعد ذلك.

ثم اجعل التكرار مستبعدًا قدر الإمكان:

  • أضف ماسحًا قبل كل commit مثل gitleaks.
  • فعّل حماية الدفع والفحص عن الأسرار في مزود Git لديك.
  • تجنب نشر خرائط المصدر في الإنتاج، أو قدّمها فقط لأداة تتبع الأخطاء لديك.
  • أضف خطوة CI تبحث في الحزمة المبنية عن بادئات التوكنات المعروفة، وتُفشل البناء عند وجود تطابق.

دقّق حزمتك باستخدام نموذج لغوي

النموذج اللغوي الكبير عين ثانية سريعة لهذه المهمة. إليك كيف تستخدم Claude Sonnet 5 على PicassoIA لاكتشاف التسريبات وصياغة وكيلك:

  1. ابنِ وافحص أولًا. شغّل npm run build، ثم grep -rE "sk_|pk_|pia_sk_|Bearer " dist/ لالتقاط الحالات الواضحة بنفسك.
  2. افتح صفحة النموذج. انتقل إلى Claude Sonnet 5 على PicassoIA.
  3. الصق الكود بعد حجب القيم فقط. ضمّن الملفات التي تجري استدعاءات الشبكة، مع استبدال كل قيمة حقيقية بالنص REDACTED. لا تلصق أي سر حي في أي أداة دردشة.
  4. اطرح سؤالًا محددًا. مثلًا: "اذكر كل مكان يرسل فيه هذا الكود بيانات اعتماد من المتصفح، وأعد كتابة كل موضع ليستدعي مسار خادم بدلًا من ذلك."
  5. راجع الإجابة وفق القواعد أعلاه. تحقق من التحقق من المدخلات وحدود الاستخدام ومعالجة الأخطاء، ثم اختبر في DevTools.

💡 نصيحة: للحصول على رأي ثانٍ، شغّل الأمر نفسه على GPT 5.6 Sol أو احصل على مراجعة أولية سريعة من Gemini 3.5 Flash. تلتقط النماذج المختلفة أخطاء مختلفة.

ابنِ تطبيقات الصور دون تسريب التوكنات

مصمم مبتسم عند مكتب استوديو مضيء ينظر إلى شاشة مليئة بالصور

ينطبق كل ما سبق بقوة أكبر عندما ينتج تطبيقك صورًا أو فيديو، لأن كل استدعاء يستهلك وقت GPU. يبقى النمط نفسه: تجمع الصفحة الأمر النصي، ويحتفظ وكيلك ببيانات الاعتماد، وتتولى PicassoIA التصيير.

اختر النموذج الذي يناسب منتجك. يناسب Flux 2 Pro الواقعية الفوتوغرافية التفصيلية، ويتعامل Seedream 4.5 مع الأوامر ذات العناصر الكثيرة، وP-Image خيار سريع للمعاينات، وGPT Image 2 قوي في النصوص داخل الصور. أما الحركة فتصفح نماذج تحويل النص إلى فيديو في الفهرس الكامل لنماذج PicassoIA.

إليك قائمة سريعة قبل نشرك التالي:

  • لا يظهر أي بيان اعتماد في الحزمة المبنية أو في خرائط المصدر.
  • يستدعي المتصفح وكيلك، لا المزود أبدًا.
  • يُتحقق من طول الأمر النصي واختيار النموذج وحجم المخرجات على الخادم.
  • حدود الاستخدام وسقف إنفاق المزود مفعّلان.
  • أي توكن عام مقيّد بالنطاق والصلاحية والحصة.
  • تعرف خطوات إلغاء كل توكن قبل أن تحتاجها.

هل أنت مستعد لرؤيته يعمل؟ افتح PicassoIA، وولّد بعض الصور باستخدام النماذج أعلاه، ثم اربط الأمر النصي نفسه بوكيلك الخاص. جرّب أساليب وأوامر مختلفة، وانشر تطبيقًا لا يستطيع الزوار نسخ شيء منه من المتصفح سوى الصورة النهائية.

شارك هذا المقال

اختر لغتك

مقالات ذات صلة