طَبِّقْهاتعلّم المبدأ، وطبّقه بيدكمقرر 121 · مبادئ قواعد البيانات
الدرس 6 · الوحدة 3: أمان وتكامل قواعد البيانات

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

من يرى ماذا؟ وكيف نحمي القاعدة من الحذف والتسريب والحقن؟ ⏱ نحو 25 دقيقة.

بعد هذا الدرس ستستطيع أن:
  • تعدّد أشهر تهديدات قواعد البيانات وحماية كل منها.
  • تقرأ أوامر المستخدمين والصلاحيات في MySQL: CREATE USER و GRANT و REVOKE.
  • تنشئ عرضًا View يخفي الأعمدة الحساسة.
  • تشرح حقن SQL وعلاجه، وطرق ربط القاعدة بالتطبيقات.

١لماذا نحمي قاعدة البيانات؟

قاعدة البيانات أثمن ما في النظام: أرقام جوالات العملاء، ودرجات الطلاب، والمبالغ المالية. وأشهر ما يهددها:

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

٢المستخدمون والصلاحيات

في MySQL لكل شخص أو تطبيق حساب (مصادقة Authentication)، ولكل حساب صلاحيات محددة (تفويض Authorization). وتُدار بأوامر عائلة DCL:

mysql-security.sql
-- إنشاء حساب للكاشيرCREATE USER 'cashier'@'localhost' IDENTIFIED BY 'Strong#Pass2025'; -- يقرأ المنتجات فقطGRANT SELECT ON store.products TO 'cashier'@'localhost'; -- ويضيف الطلباتGRANT INSERT ON store.orders TO 'cashier'@'localhost'; -- عرض صلاحياته، ثم سحب واحدةSHOW GRANTS FOR 'cashier'@'localhost';REVOKE INSERT ON store.orders FROM 'cashier'@'localhost';

هذه الأوامر خاصة بخادم MySQL، ولا تعمل في SQLite لأنه ملف واحد بلا مستخدمين. نفّذها في المعمل.

٣العرض View: نافذة على جزء من البيانات

أحيانًا نريد أن يرى قسمٌ بعض الأعمدة فقط. العرض View استعلام محفوظ باسم، يُستخدم كأنه جدول. هنا قسم التسويق يرى أسماء العملاء ومدنهم، دون أرقام الجوالات:

query.sql
CREATE VIEW customer_cities ASSELECT name, cityFROM customers; SELECT * FROM customer_cities;
النتيجة · 10 صفوف
namecity
سعود العتيبيالمدينة المنورة
نورة القحطانيالرياض
خالد الحربيالمدينة المنورة
ريم الشهريجدة
فهد الدوسريالرياض
سارة المطيريالدمام
عبدالله الزهرانيجدة
هند السبيعيالمدينة المنورة
ماجد الغامديتبوك
لمى العنزيNULL

في MySQL نمنح التسويق صلاحية على العرض وحده: GRANT SELECT ON store.customer_cities TO 'marketing'، فلا يصل إلى جدول العملاء الأصلي.

٤حقن SQL: أخطر ثغرة

تخيّل موقعًا يبحث عن عميل باسمه، ويبني الاستعلام بلصق ما كتبه الزائر. زائر عادي يكتب «سعود العتيبي». لكن مهاجمًا يكتب في الخانة:

x' OR '1'='1

فيصبح الاستعلام الذي يصل القاعدة:

query.sql
SELECT name, phone FROM customers WHERE name = 'x' OR '1'='1';
النتيجة · 10 صفوف
namephone
سعود العتيبي0551000001
نورة القحطاني0551000002
خالد الحربي0551000003
ريم الشهري0551000004
فهد الدوسري0551000005
سارة المطيري0551000006
عبدالله الزهرانيNULL
هند السبيعي0551000008
ماجد الغامدي0551000009
لمى العنزي0551000010

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

والعلاج: لا تلصق مدخلات المستخدم داخل نص الاستعلام. استخدم الاستعلام ذا المعاملات Parameterized Query: الاستعلام ثابت، والقيمة تُرسل منفصلة فلا تُفهم أبدًا على أنها أوامر:

✗ خطير
app.py
# ✗ خطير: نلصق ما كتبه المستخدم داخل الاستعلامname = input("اسم العميل: ")sql = "SELECT * FROM customers WHERE name = '" + name + "'"cursor.execute(sql)
✓ آمن
app.py
# ✓ آمن: الاستعلام ثابت، والقيمة تُمرَّر منفصلةname = input("اسم العميل: ")sql = "SELECT * FROM customers WHERE name = %s"cursor.execute(sql, (name,))

٥النسخ الاحتياطي

النسخة الاحتياطية Backup ملف يحفظ القاعدة كلها لتستعيدها عند الكارثة. في MySQL تُنشأ بأداة mysqldump من سطر الأوامر:

terminal
-- (تُكتب في سطر الأوامر لا في محرر SQL)mysqldump -u root -p store > store_backup.sql   -- نسخmysql -u root -p store < store_backup.sql       -- استعادة

٦تكامل القاعدة مع التطبيقات

القاعدة لا تعمل وحدها: الموقع وتطبيق الجوال ونظام المحاسبة يتصلون بها. يتصل التطبيق بالخادم بعنوانه، واسم مستخدم خاص به، ثم يرسل الاستعلامات ويستقبل النتائج. هذا تطبيق بايثون يتصل بـ MySQL:

app.py
import mysql.connector db = mysql.connector.connect(    host="localhost",    user="shop_app",        # مستخدم خاص بالتطبيق، لا root    password="********",    database="store")cursor = db.cursor()cursor.execute("SELECT name, price FROM products WHERE stock > %s", (0,))for name, price in cursor.fetchall():    print(name, price)
طريقة التكاملمتى تُستخدم؟
اتصال مباشر (مكتبة مثل mysql-connector أو PDO في PHP)التطبيق الخلفي للموقع يقرأ ويكتب مباشرة.
واجهة برمجية APIتطبيق الجوال لا يتصل بالقاعدة؛ يطلب من خادم وسيط يتصل هو بها. أكثر أمانًا.
تصدير واستيراد CSV أو JSONنقل البيانات إلى نظام آخر، أو إلى Excel للتحليل.

ستبني اتصالًا كهذا بنفسك في مقرر «تقنيات تطوير الواجهات الخلفية».

توقّع وتحقّق

الأمر الذي يمنح صلاحية في MySQL:

DCL.

أهم علاج لحقن SQL:

القيمة منفصلة عن الأمر.

مبدأ أقل الصلاحيات يعني:

Least Privilege.

العرض View هو:

نافذة على البيانات.

الخلاصة

تحدٍّ برمجي: عرض آمن لقسم الشحن

متوسط

قسم الشحن يحتاج اسم العميل ومدينته ورقم الطلب وحالته، ولا يجوز أن يرى أرقام الجوالات. أنشئ عرضًا لهم.

المطلوب:
  • أنشئ عرضًا اسمه shipping_view من جدول الطلبات orders وجدول العملاء.
  • أعمدته: رقم الطلب، واسم العميل، ومدينته، وحالة الطلب، بهذا الترتيب.
  • لا تُدخل رقم الجوال.
👀 اعرض النتيجة المطلوبة
بعد التنفيذ: SELECT * FROM shipping_view
idnamecitystatus
1سعود العتيبيالمدينة المنورةمُسلَّم
2نورة القحطانيالرياضمُسلَّم
3سعود العتيبيالمدينة المنورةمُسلَّم
4خالد الحربيالمدينة المنورةمُسلَّم
5ريم الشهريجدةملغى
6فهد الدوسريالرياضمُسلَّم
7نورة القحطانيالرياضقيد الشحن
8سارة المطيريالدمامقيد الشحن
9هند السبيعيالمدينة المنورةجديد
10سعود العتيبيالمدينة المنورةجديد
11ماجد الغامديتبوكجديد
12خالد الحربيالمدينة المنورةجديد
✓ التحقق الآلي من حلّك:
  • الكود يعمل بلا أخطاء
  • العرض يعرض الأعمدة الأربعة المطلوبة لكل طلب
  • لا يظهر رقم الجوال

💡 تلميح 1
الكود الأولي فيه الربط بين الجدولين جاهزًا؛ أكمل الأعمدة فقط.
💡 تلميح 2
customers.city و orders.status

حاول بنفسك أولًا؛ للمسألة أكثر من حلّ صحيح.

solution.sql
CREATE VIEW shipping_view ASSELECT orders.id, customers.name, customers.city, orders.statusFROM ordersJOIN customers ON customers.id = orders.customer_id; SELECT * FROM shipping_view;
انتقل إلى تقييم الدرساختيار من متعدد، وصح وخطأ، وأكمل الفراغ، وتتبّع الناتج.ابدأ التقييم ←

📝 معلم؟ أرسل ملاحظة للمؤلف عن هذا الدرس