تخطَّ إلى المحتوى
Uttra

هل التطبيقات المبنية بالذكاء الاصطناعي آمنة؟ وكيف تحميها

التطبيق المبني بالذكاء الاصطناعي ليس آمنًا أو خطرًا بطبيعته، لكنه يكرر أخطاء معروفة: مفاتيح سرية في الواجهة، وجداول Supabase بلا سياسات RLS، ومسارات API لا تتحقق من المستخدم، وغياب تحديد معدل الطلبات، وثغرات حقن و XSS. تراجعها بقائمة فحص قصيرة قبل النشر وتصلح كل بند بكود محدد.

آخر تحديث:

لماذا يحدث هذا: الكود يعمل، لكنه يثق بالجميع

أدوات بناء التطبيقات بالذكاء الاصطناعي تُقاس بسؤال واحد: هل ظهرت الميزة وعملت؟ والطريق الأقصر إلى «تعمل» غالبًا يتجاوز الحماية: استدعاء الخدمة من المتصفح مباشرة بدل الخادم، وفتح الجدول للقراءة والكتابة حتى لا يظهر خطأ صلاحيات، وقبول هوية المستخدم من جسم الطلب لأن ذلك أسهل من التحقق من الجلسة. كل هذا ينجح في المعاينة، ثم يصبح ثغرة يوم تنشر.

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

ما الذي تحتاج أن تبحث عنه: خمس ثغرات متكررة

قائمة الفحص الأساسية قبل النشر
الثغرةكيف تكتشفهاالإصلاح
مفتاح سري في الواجهةابحث في ملفات الواجهة عن sk_ أو secret أو service_roleانقل الاستدعاء إلى الخادم واقرأ المفتاح من متغيّر بيئة
جدول Supabase بلا RLSلوحة Supabase تُظهر الجدول بلا سياسات أو RLS معطّلفعّل RLS واكتب سياسة لكل عملية
مسار API بلا تحققافتح المسار بدون تسجيل دخول أو بحساب مستخدم آخرتحقق من الجلسة واجلب userId منها لا من الطلب
لا تحديد لمعدل الطلباتأرسل 100 طلب تسجيل دخول متتالي، هل تُقبل كلها؟Rate limit على تسجيل الدخول والنماذج والمسارات المكلفة
حقن SQL أو XSSابحث عن استعلامات تُبنى بدمج النص، أو innerHTML مع مدخلات المستخدماستعلامات بمعاملات، و textContent بدل innerHTML

مفتاح anon في Supabase مصمم ليظهر في الواجهة، وأمانه كله معتمد على سياسات RLS. أما مفتاح service_role فيتجاوز RLS بالكامل، ووجوده في كود المتصفح يعني أن قاعدة بياناتك مفتوحة لأي زائر.

الحل خطوة بخطوة

  1. افحص المفاتيح في الواجهة والمستودع

    شغّل بحثًا سريعًا في مجلدات الواجهة وسجل git. إن وجدت مفتاحًا سريًا سبق رفعه، فلا يكفي حذفه: ألغِه من لوحة الخدمة وأصدر مفتاحًا جديدًا.

    code
    grep -rnE "sk_(live|test)_|service_role|SECRET|PRIVATE_KEY" src/ public/ --include=*.{js,ts,tsx,jsx,html}
    git log -p | grep -nE "sk_live_|service_role" | head
  2. فعّل RLS على كل جدول فيه بيانات مستخدمين

    سياسة لكل عملية، تربط الصف بصاحبه عبر auth.uid(). ابدأ بالمنع ثم افتح ما تحتاجه فقط.

  3. تحقق من الهوية في كل مسار API

    المسار يقرأ المستخدم من الجلسة أو الرمز المتحقَّق منه، ثم يتأكد أن السجل المطلوب يخصه. لا تقبل userId من جسم الطلب.

  4. حدّد معدل الطلبات

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

  5. عقّم المدخلات والمخرجات

    استعلامات بمعاملات دائمًا، وعرض نص المستخدم كنص لا كـ HTML، وتحقق من نوع وطول كل حقل في الخادم.

Supabase: سياسات RLS لجدول مواعيد يملك كل مريض مواعيده
alter table public.appointments enable row level security;

create policy "patients read own appointments"
  on public.appointments for select
  to authenticated
  using (patient_id = (select auth.uid()));

create policy "patients create own appointments"
  on public.appointments for insert
  to authenticated
  with check (patient_id = (select auth.uid()));

create policy "patients cancel own future appointments"
  on public.appointments for update
  to authenticated
  using (patient_id = (select auth.uid()) and starts_at > now())
  with check (patient_id = (select auth.uid()));

-- No delete policy: deletes are refused for everyone except the service role.
Express: تحديد معدل الطلبات مع express-rate-limit
// npm i express-rate-limit
import express from "express";
import rateLimit from "express-rate-limit";

const app = express();
app.set("trust proxy", 1); // behind a reverse proxy: use the real client IP

const loginLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  limit: 10,                // 10 attempts per IP per window
  standardHeaders: "draft-7",
  legacyHeaders: false,
  message: { error: "محاولات كثيرة، حاول بعد قليل" },
});

const apiLimiter = rateLimit({ windowMs: 60 * 1000, limit: 120 });

app.use("/api/", apiLimiter);
app.post("/api/login", loginLimiter, express.json(), loginHandler);
Express: التحقق من صاحب السجل وحماية الاستعلام
// Bad: trusts the client
// app.get("/api/orders/:id", (req, res) => db.query(`SELECT * FROM orders WHERE id = ${req.params.id}`))

app.get("/api/orders/:id", requireSession, async (req, res) => {
  const id = Number.parseInt(req.params.id, 10);
  if (!Number.isInteger(id)) return res.status(400).end();
  const order = await db.get(
    "SELECT id, total, status FROM orders WHERE id = ? AND user_id = ?",
    [id, req.session.userId] // parameters, and the owner comes from the session
  );
  if (!order) return res.status(404).end();
  res.json(order);
});
الواجهة: عرض نص المستخدم بأمان
// Bad: el.innerHTML = review.text;
const el = document.createElement("p");
el.textContent = review.text; // rendered as text, scripts never run
list.append(el);
برومبت مراجعة أمنية لمشروع قائمجرّبه في أوترا

راجع المشروع أمنيًا قبل النشر: ابحث عن أي مفتاح سري في ملفات الواجهة وانقله إلى الخادم كمتغيّر بيئة، وتأكد أن كل مسار API يتحقق من الجلسة ويقرأ المستخدم منها لا من الطلب، وأضف تحديد معدل الطلبات على تسجيل الدخول والنماذج، واستبدل أي استعلام مبني بدمج النص باستعلام بمعاملات، وأي innerHTML لمدخلات المستخدم بـ textContent. اكتب لي قائمة بما وجدته وما أصلحته.

قبل النشر وبعده: روتين أمان بسيط

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

  • بعد كل ميزة تضيف جدولًا: تأكد أن RLS أو التحقق من الملكية مفعّل عليه قبل أن تنتقل للميزة التالية.
  • بعد كل ميزة تضيف مسار API: افتحه من نافذة خاصة بدون تسجيل دخول، ثم بحساب مستخدم آخر، ويجب أن يُرفض في الحالتين.
  • كل شهر: راجع من لديه صلاحية الوصول إلى المشروع ولوحات الخدمات، واحذف من لم يعد يعمل معك.
  • عند أي شك في تسريب: ألغِ المفتاح وأصدر بديلًا أولًا، ثم ابحث عن السبب.
  • في السجلات: سجّل محاولات الدخول الفاشلة والطلبات المرفوضة بتحديد المعدل، فهي أول إشارة إلى من يجرّب على تطبيقك.

الحل الأسرع: أوترا

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

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

جرّبه في أوتراجرّبه في أوترا

بوضع Review، افحص مشروعي بحسب هذه القائمة: مفاتيح في الواجهة، مسارات API بلا تحقق من المستخدم، غياب تحديد معدل الطلبات، استعلامات قابلة للحقن، وعرض مدخلات المستخدم كـ HTML. أصلح ما تجده واكتب ملخصًا قصيرًا بالعربية.

أسئلة شائعة

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

صفحات ذات صلة