الأمان والأداء

أمان قاعدة البيانات

18 دقيقة الدرس 13 من 35

فهم أمان قاعدة البيانات

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

تأثير خرق البيانات: متوسط تكلفة خرق البيانات في 2024: 4.45 مليون دولار. 83% من المؤسسات عانت من أكثر من خرق بيانات واحد. أمان قاعدة البيانات ليس اختيارياً.

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

نفذ تحكم صارم في الوصول للحد من من يمكنه الوصول إلى البيانات:

-- التحكم في الوصول لـ MySQL/MariaDB

-- إنشاء مستخدم قاعدة بيانات مع امتيازات محدودة
CREATE USER 'app_user'@'localhost' IDENTIFIED BY 'StrongP@ssw0rd123!';

-- منح الامتيازات الضرورية فقط
GRANT SELECT, INSERT, UPDATE, DELETE ON myapp.* TO 'app_user'@'localhost';

-- مستخدم للقراءة فقط للتقارير
CREATE USER 'report_user'@'localhost' IDENTIFIED BY 'AnotherStr0ngP@ss!';
GRANT SELECT ON myapp.* TO 'report_user'@'localhost';

-- مستخدم التطبيق - بدون امتيازات DDL
CREATE USER 'web_app'@'192.168.1.%' IDENTIFIED BY 'C0mpl3xP@ssword!';
GRANT SELECT, INSERT, UPDATE, DELETE ON myapp.users TO 'web_app'@'192.168.1.%';
GRANT SELECT, INSERT, UPDATE, DELETE ON myapp.posts TO 'web_app'@'192.168.1.%';

-- تطبيق التغييرات
FLUSH PRIVILEGES;

-- عرض امتيازات المستخدم
SHOW GRANTS FOR 'app_user'@'localhost';

-- إلغاء الامتيازات
REVOKE DELETE ON myapp.* FROM 'app_user'@'localhost';

-- إزالة المستخدم
DROP USER 'old_user'@'localhost';
تحذير: لا تستخدم مستخدم الجذر أبداً لاتصالات التطبيق. لا تمنح ALL PRIVILEGES أبداً إلا إذا كان ذلك ضرورياً للغاية. قم دائماً بتحديد وصول المستخدم إلى مضيفين محددين، وليس '%'.

أمان كلمة المرور لمستخدمي قاعدة البيانات

-- تعيين سياسات كلمة مرور قوية (MySQL 8.0+)
INSTALL COMPONENT 'file://component_validate_password';

-- تكوين التحقق من كلمة المرور
SET GLOBAL validate_password.policy = STRONG;
SET GLOBAL validate_password.length = 12;
SET GLOBAL validate_password.mixed_case_count = 1;
SET GLOBAL validate_password.number_count = 1;
SET GLOBAL validate_password.special_char_count = 1;

-- انتهاء صلاحية كلمة المرور
ALTER USER 'app_user'@'localhost' PASSWORD EXPIRE INTERVAL 90 DAY;

-- فرض تغيير كلمة المرور في تسجيل الدخول التالي
ALTER USER 'temp_user'@'localhost' PASSWORD EXPIRE;

-- منع إعادة استخدام كلمة المرور
SET GLOBAL password_history = 6;
SET GLOBAL password_reuse_interval = 365;

-- قفل الحساب بعد محاولات تسجيل دخول فاشلة
ALTER USER 'app_user'@'localhost' FAILED_LOGIN_ATTEMPTS 3 PASSWORD_LOCK_TIME 2;

التشفير في حالة السكون

قم بتشفير ملفات قاعدة البيانات على القرص للحماية من السرقة المادية أو كشف النسخ الاحتياطي:

# تشفير بيانات MySQL الشفاف (TDE)

# 1. إنشاء المفتاح الرئيسي
sudo mkdir -p /var/lib/mysql-keyring
sudo chown mysql:mysql /var/lib/mysql-keyring

# 2. تكوين my.cnf
[mysqld]
early-plugin-load=keyring_file.so
keyring_file_data=/var/lib/mysql-keyring/keyring

# 3. إعادة تشغيل MySQL
sudo systemctl restart mysql

# 4. تشفير الجداول الموجودة
ALTER TABLE users ENCRYPTION='Y';
ALTER TABLE sensitive_data ENCRYPTION='Y';

# 5. تعيين التشفير الافتراضي للجداول الجديدة
SET GLOBAL default_table_encryption=ON;

# 6. التحقق من حالة التشفير
SELECT TABLE_SCHEMA, TABLE_NAME, CREATE_OPTIONS
FROM information_schema.TABLES
WHERE CREATE_OPTIONS LIKE '%ENCRYPTION%';
# تشفير PostgreSQL في حالة السكون

# 1. تهيئة المجموعة مع التشفير
initdb -D /var/lib/postgresql/data --data-checksums

# 2. تكوين postgresql.conf
ssl = on
ssl_cert_file = '/path/to/server.crt'
ssl_key_file = '/path/to/server.key'
ssl_ca_file = '/path/to/root.crt'

# 3. تشفير أعمدة محددة (pgcrypto)
CREATE EXTENSION pgcrypto;

-- تشفير البيانات
INSERT INTO users (email, password)
VALUES ('user@example.com', crypt('password', gen_salt('bf')));

-- التحقق من كلمة المرور
SELECT * FROM users
WHERE email = 'user@example.com'
AND password = crypt('password', password);

التشفير أثناء النقل (SSL/TLS)

# تكوين MySQL SSL/TLS

# 1. إنشاء شهادات SSL
mysql_ssl_rsa_setup --datadir=/var/lib/mysql

# 2. تكوين my.cnf
[mysqld]
require_secure_transport=ON
ssl-ca=/var/lib/mysql/ca.pem
ssl-cert=/var/lib/mysql/server-cert.pem
ssl-key=/var/lib/mysql/server-key.pem

# 3. طلب SSL لمستخدم محدد
ALTER USER 'app_user'@'%' REQUIRE SSL;

# 4. التحقق من اتصال SSL
SHOW STATUS LIKE 'Ssl_cipher';
SHOW VARIABLES LIKE '%ssl%';

# 5. الاتصال بـ SSL (من التطبيق)
mysql --ssl-mode=REQUIRED \
--ssl-ca=/path/to/ca.pem \
-h database.example.com \
-u app_user -p
// اتصال Node.js MySQL مع SSL
const mysql = require('mysql2');
const fs = require('fs');

const connection = mysql.createConnection({
host: 'database.example.com',
user: 'app_user',
password: process.env.DB_PASSWORD,
database: 'myapp',
ssl: {
ca: fs.readFileSync('/path/to/ca.pem'),
cert: fs.readFileSync('/path/to/client-cert.pem'),
key: fs.readFileSync('/path/to/client-key.pem'),
rejectUnauthorized: true
}
});
أفضل ممارسة: استخدم دائماً SSL/TLS لاتصالات قاعدة البيانات، خاصة عند الاتصال عبر الإنترنت أو الشبكات غير الموثوقة. ارفض الاتصالات بدون SSL في الإنتاج.

أمان النسخ الاحتياطي

يمكن أن تكون النسخ الاحتياطية لقاعدة البيانات منجم ذهب للمهاجمين إذا لم يتم تأمينها بشكل صحيح:

# نص نسخ احتياطي آمن لـ MySQL
#!/bin/bash

BACKUP_DIR="/var/backups/mysql"
DATE=$(date +%Y%m%d_%H%M%S)
DB_NAME="myapp"
ENCRYPTION_KEY="your-strong-encryption-key"

# إنشاء دليل النسخ الاحتياطي مع أذونات مقيدة
mkdir -p $BACKUP_DIR
chmod 700 $BACKUP_DIR

# تفريغ قاعدة البيانات
mysqldump --single-transaction \
--routines --triggers \
--user=backup_user \
--password=$DB_PASSWORD \
$DB_NAME | gzip > $BACKUP_DIR/${DB_NAME}_${DATE}.sql.gz

# تشفير النسخة الاحتياطية
openssl enc -aes-256-cbc -salt \
-in $BACKUP_DIR/${DB_NAME}_${DATE}.sql.gz \
-out $BACKUP_DIR/${DB_NAME}_${DATE}.sql.gz.enc \
-k $ENCRYPTION_KEY

# إزالة النسخة الاحتياطية غير المشفرة
rm $BACKUP_DIR/${DB_NAME}_${DATE}.sql.gz

# تعيين أذونات مقيدة
chmod 600 $BACKUP_DIR/${DB_NAME}_${DATE}.sql.gz.enc

# إزالة النسخ الاحتياطية الأقدم من 30 يوماً
find $BACKUP_DIR -name "*.enc" -mtime +30 -delete

# التحميل إلى تخزين خارج الموقع آمن
aws s3 cp $BACKUP_DIR/${DB_NAME}_${DATE}.sql.gz.enc \
s3://secure-backup-bucket/ \
--server-side-encryption AES256

تدقيق قاعدة البيانات

-- MySQL Enterprise Audit (تجاري) أو MariaDB Audit Plugin

-- تثبيت مكون التدقيق (MariaDB)
INSTALL SONAME 'server_audit';

-- تكوين تسجيل التدقيق
SET GLOBAL server_audit_logging = ON;
SET GLOBAL server_audit_events = 'CONNECT,QUERY,TABLE';
SET GLOBAL server_audit_incl_users = 'app_user,admin_user';

-- موقع ملف السجل
SET GLOBAL server_audit_file_path = '/var/log/mysql/audit.log';
SET GLOBAL server_audit_file_rotate_size = 1000000;

-- الاستعلام عن سجل التدقيق
SELECT * FROM mysql.audit_log
WHERE command_class = 'drop_table'
AND timestamp > NOW() - INTERVAL 1 DAY;
-- تدقيق PostgreSQL مع امتداد pgAudit
CREATE EXTENSION pgaudit;

-- تكوين postgresql.conf
shared_preload_libraries = 'pgaudit'
pgaudit.log = 'all' -- أو 'ddl', 'write', 'read'
pgaudit.log_catalog = off
pgaudit.log_parameter = on

-- تدقيق قاعدة بيانات محددة
ALTER DATABASE myapp SET pgaudit.log = 'write, ddl';

-- عرض سجلات التدقيق
SELECT * FROM pg_stat_statements
WHERE query LIKE '%DELETE%' OR query LIKE '%DROP%';

إخفاء البيانات وإخفاء الهوية

-- إخفاء بيانات MySQL (MySQL 8.0+)

-- إنشاء عرض مقنع للمطورين
CREATE VIEW users_masked AS
SELECT
id,
CONCAT(LEFT(email, 3), '***@***.com') AS email,
'*******' AS password,
CONCAT(LEFT(name, 1), '***') AS name,
created_at
FROM users;

-- منح الوصول إلى العرض المقنع فقط
GRANT SELECT ON myapp.users_masked TO 'developer'@'localhost';

-- إخفاء قائم على الوظائف
DELIMITER $$
CREATE FUNCTION mask_email(email VARCHAR(255))
RETURNS VARCHAR(255) DETERMINISTIC
BEGIN
DECLARE masked VARCHAR(255);
SET masked = CONCAT(
LEFT(email, 2),
'***@',
SUBSTRING_INDEX(email, '@', -1)
);
RETURN masked;
END$$
DELIMITER ;
تمرين: نفذ أمان قاعدة بيانات شامل:
  1. أنشئ مستخدمين منفصلين للتطبيق والنسخ الاحتياطية والوصول للقراءة فقط
  2. فعّل SSL/TLS لجميع اتصالات قاعدة البيانات
  3. نفذ التشفير في حالة السكون للجداول الحساسة
  4. أنشئ نص نسخ احتياطي مشفر آلي
  5. فعّل تسجيل تدقيق قاعدة البيانات
  6. أنشئ عروضاً مقنعة للبيئات غير الإنتاجية
  7. اختبر إجراء استعادة النسخة الاحتياطية