عندما قرأت المادة 32 من GDPR لأول مرة، ارتكبت خطأً. ظننت أنها وثيقة قانونية بحتة. لكنها ليست كذلك؛ إنها مواصفات للبنية التحتية. تنص اللائحة على أنك بحاجة إلى "تدابير تقنية مناسبة" لحماية البيانات الشخصية. هذه العبارة مخيفة بسبب غموضها. ماذا يعني "مناسبة"؟ ما الذي يعتبر "تدبيراً تقنياً"؟ ومن يقرر ما إذا كنت قد فعلت ما يكفي؟
سيقدم لك مستشار الامتثال وثيقة سياسة من 50 صفحة. سيتجاهلها المدقق ويطلب مخطط قاعدة بياناتك. هذا الدليل هو الحل الوسط. لقد قمت بتطبيق ضوابط المادة 32 لأكثر من 12 شركة SaaS. تظهر نفس الضوابط التسعة في كل مرة. وتظهر نفس الأسئلة الثلاثة للمدقق في كل مرة. هذا دليل شامل للضوابط التقنية التسعة التي يجب عليك تطبيقها، والكود والأوامر الدقيقة لكل منها، والأسئلة التي سيطرحها مدقق GDPR الخاص بك.
جدول المحتويات
- ماذا ستتعلم
- المتطلبات المسبقة
- الجزء الأول: فهم المادة 32
- الجزء الثاني: المادة 32(1)(أ) — إخفاء الهوية المستعار (Pseudonymisation) والتشفير (Encryption)
- الجزء الثالث: المادة 32(1)(ب) — السرية (Confidentiality) والنزاهة (Integrity)
- الجزء الرابع: المادة 32(1)(ج) — التوفر (Availability) والمرونة (Resilience)
- الجزء الخامس: المادة 32(1)(د) — الاختبار المنتظم
- الجزء السادس: أفضل ممارسات اختبار الاختراق (Penetration Testing)
- ملخص
- الخطوات التالية
- المصادر
ماذا ستتعلم
- الضوابط التقنية التسعة المطلوبة بموجب المادة 32(1)(أ) حتى (د) من GDPR.
- أوامر PostgreSQL الدقيقة لإخفاء الهوية المستعار (Pseudonymisation) والتشفير على مستوى الحقل (field-level encryption).
- كيفية تطبيق تسجيل الخروج التلقائي (automatic logoff) وتحديد هوية المستخدم الفريدة (unique user identification).
- تسجيل التدقيق على مستوى التطبيق (Application-level audit logging) الذي يتجاوز CloudTrail.
- ضوابط النزاهة (Integrity controls) التي تثبت أن البيانات لم يتم تغييرها.
- mTLS و TLS 1.3 لأمان النقل (transmission security).
- الأسئلة الخمسة للمدقق التي يجب عليك الإجابة عليها بالأدلة.
دعنا نتعمق.
المتطلبات المسبقة
قبل المتابعة، يجب أن يكون لديك:
المعرفة:
- إلمام بـ PostgreSQL و SQL الأساسي.
- فهم أساسي لخدمات AWS (KMS, RDS, CloudTrail).
- القدرة على قراءة كود Python و JavaScript/Node.js.
- معرفة عملية بماهية GDPR — إذا كنت تبدأ من الصفر، اقرأ نظرة عامة على GDPR من ICO أولاً.
الأدوات والوصول:
- PostgreSQL 14 أو أحدث.
- حساب AWS مع وصول IAM administrator.
- Python 3.8 أو أحدث مع مكتبة
cryptography(pip install cryptography). - Node.js 16 أو أحدث.
- أداة أتمتة الامتثال — Vanta أو OneTrust — اختيارية ولكن يوصى بها لجمع الأدلة.
الوقت المقدر:
تستغرق الضوابط في هذا الدليل من 2 إلى 4 أسابيع لتطبيقها بالكامل، اعتماداً على البنية التحتية الحالية لديك. تتراوح الضوابط الفردية من 30 دقيقة (إعداد مفتاح KMS) إلى 5 أيام (نشر التشفير الكامل على مستوى التطبيق).
الجزء الأول: فهم المادة 32 — المتطلبات التقنية
1.1. ما تتطلبه المادة 32 فعلياً
المادة 32 من GDPR بعنوان "أمن المعالجة". تتطلب من المتحكمين والمعالجين تنفيذ "تدابير تقنية وتنظيمية مناسبة" لضمان مستوى أمان يتناسب مع المخاطر. إليك التمييز المهم الذي يغفله معظم الفرق:
المادة 32 ليست قائمة مراجعة للسياسات. السياسة تقول "نقوم بتشفير البيانات الشخصية". الدليل يقول "هذا هو مفتاح KMS مع التدوير التلقائي، وهذا هو كود التشفير على مستوى التطبيق، وهذه هي سجلات CloudTrail التي تظهر كل محاولة فك تشفير". المدقق يريد دليلاً، وليس وثائق.
المتطلبات الأربعة الرئيسية:
| القسم | المتطلب | ماذا يعني للمهندسين |
|---|---|---|
| 32(1)(أ) | إخفاء الهوية المستعار (Pseudonymisation) والتشفير (Encryption) | يجب تخزين البيانات الشخصية بحيث لا يمكن نسبتها إلى موضوع بيانات محدد دون معلومات إضافية محفوظة بشكل منفصل. |
| 32(1)(ب) | السرية (Confidentiality)، النزاهة (Integrity)، التوفر (Availability)، والمرونة (Resilience) | يجب أن تحمي الأنظمة البيانات من الوصول غير المصرح به، التغيير، الفقدان، وأن تكون قادرة على التعافي من الحوادث. |
| 32(1)(ج) | استعادة التوفر والوصول | يجب أن تكون قادراً على استعادة البيانات واستعادة الوصول إلى النظام بعد حادث مادي أو تقني. |
| 32(1)(د) | الاختبار المنتظم وتقييم المخاطر | يجب أن يكون لديك عملية لاختبار وتقييم فعالية تدابير الأمان الخاصة بك بانتظام. |
1.2. سؤال النطاق: ما هي البيانات المشمولة؟
قبل تطبيق أي ضوابط، يجب أن تعرف ما هي البيانات التي تقع ضمن نطاق المادة 32. تنطبق اللائحة على البيانات الشخصية — أي معلومات يمكن أن تحدد هوية فرد حي بشكل مباشر أو غير مباشر.
أنواع البيانات ومستويات حمايتها:
| الفئة | أمثلة | مستوى الحماية |
|---|---|---|
| بيانات شخصية (Personal data) | الاسم، البريد الإلكتروني، رقم الهاتف، عنوان IP | قياسي (Standard) |
| بيانات شخصية حساسة (Sensitive personal data) | بيانات صحية، بيانات بيومترية، آراء سياسية، معتقدات دينية | معزز (Enhanced) |
| بيانات مخفية الهوية المستعار (Pseudonymised data) | بيانات تم استبدال المعرفات المباشرة فيها برمز | قياسي (Standard) |
| بيانات مجهولة الهوية (Anonymised data) | بيانات لا يمكن إعادة تحديد هويتها تحت أي ظروف معقولة | خارج النطاق (Out of scope) |
سؤال تعيين البيانات الذي سيطرحه المدقق عليك: "هل يمكنك تقديم مخطط تدفق بيانات يوضح أين تدخل البيانات الشخصية إلى نظامك، وأين يتم تخزينها، وأين تتم معالجتها، وكيف يتم حذفها؟"
قبل أن يسأل المدقق، قم بتشغيل هذا الأمر لتوثيق جميع قواعد البيانات التي تخزن البيانات الشخصية في بيئة AWS الخاصة بك:
# List all RDS instances with their encryption status
# Any StorageEncrypted: false is a finding
aws rds describe-db-instances \
--query 'DBInstances[*].{ ID:DBInstanceIdentifier, Engine:Engine, StorageEncrypted:StorageEncrypted, Region:AvailabilityZone }' \
--output table
يجب معالجة أي مثيل يظهر StorageEncrypted: false قبل تدقيق المادة 32 الخاص بك.
الجزء الثاني: المادة 32(1)(أ) — إخفاء الهوية المستعار (Pseudonymisation) والتشفير (Encryption)
2.1. كيفية تطبيق إخفاء الهوية المستعار (Pseudonymisation) على مستوى قاعدة البيانات
يستبدل إخفاء الهوية المستعار (Pseudonymisation) المعرفات المباشرة — الأسماء، عناوين البريد الإلكتروني، أرقام جوازات السفر — باسم مستعار أو رمز. الهدف هو أن مجموعة البيانات العاملة الرئيسية لا يمكنها تحديد موضوع البيانات دون الوصول إلى جدول بحث مخزن بشكل منفصل ومحمي بشكل منفصل.
إليك النهج غير الصحيح — المعرفات المباشرة بنص واضح (plaintext):
-- Bad: Direct identifiers stored in the main working table
CREATE TABLE users (
id SERIAL PRIMARY KEY,
full_name VARCHAR(255), -- Direct identifier — should not be here
email VARCHAR(255), -- Direct identifier — should not be here
passport_number VARCHAR(50) -- Direct identifier — should not be here
);
يعني هذا النهج أن أي مهندس أو محلل أو مهاجم لديه وصول SELECT إلى جدول users يمكنه قراءة الأفراد وتحديد هويتهم على الفور. لا يوجد فصل بين بيانات العمل والبيانات التعريفية.
إليك التنفيذ الصحيح مع جدول معرفات منفصل:
-- Good: Pseudonymised main table with a separate, restricted lookup table
-- Step 1: Main working table uses only the pseudonym
CREATE TABLE users (
id SERIAL PRIMARY KEY,
pseudonym UUID DEFAULT gen_random_uuid(), -- Non-guessable pseudonym
created_at TIMESTAMP DEFAULT NOW(),
account_status VARCHAR(50) -- No direct identifiers here
);
-- Step 2: Identifier lookup table — kept separate, access restricted
CREATE TABLE user_identifiers (
pseudonym UUID PRIMARY KEY,
full_name VARCHAR(255),
email VARCHAR(255),
passport_number VARCHAR(50),
FOREIGN KEY (pseudonym) REFERENCES users(pseudonym)
);
-- Step 3: Grant minimal, role-based access
GRANT SELECT ON users TO app_role; -- Application uses pseudonym only
GRANT SELECT, INSERT, UPDATE ON user_identifiers TO identity_service_role; -- Only the identity service sees names
ما يفعله كل جزء:
gen_random_uuid()ينشئ اسم مستعار UUID من الإصدار 4 لكل مستخدم — غير قابل للتخمين ولا يمكن عكسه بدون جدول البحث.- جدول
usersالرئيسي آمن للتحليلات والتقارير والاستخدام العام للتطبيق دون الكشف عن أي معلومات تعريفية. - فقط
identity_service_roleيمكنه ربط الجدولين — يتم تعيين هذا الدور فقط للخدمة المحددة التي تتعامل مع عمليات الهوية.
سؤال المدقق الذي ستتلقاه: "كيف تضمن أن البيانات المخفية الهوية المستعار (pseudonymised data) لا يمكن إعادة تحديد هويتها من قبل طرف غير مصرح له؟"
دليلك:
-- Show that only the identity service role has access to the identifiers table
SELECT grantee, privilege_type, table_name
FROM information_schema.role_table_grants
WHERE table_name = 'user_identifiers';
-- Expected output: only identity_service_role listed
2.2. كيفية تطبيق التشفير في حالة السكون (Encryption at Rest) باستخدام مفاتيح يديرها العميل
يحمي التشفير على مستوى التخزين البيانات إذا قام شخص ما بسرقة القرص فعلياً. لكنه لا يحمي من موظف AWS مميز، أو مسؤول سحابي مخترق، أو مستخدم مصرح له بالوصول المباشر إلى قاعدة البيانات. يدرك مدققو المادة 32 هذا التمييز — وسيسألون عنه.
إليك النهج غير الصحيح — مفاتيح تديرها AWS:
# Bad: AWS-managed KMS key
# You do not control who at AWS can access the key material
aws kms create-key \
--origin AWS_KMS \
--description "AWS managed key for production"
المشكلة: عندما يسأل المدقق "هل يمكنك إثبات أن موظفي AWS لا يمكنهم فك تشفير بيانات عملائك؟"، الإجابة هي لا. مفاتيح AWS المدارة تديرها AWS.
إليك التنفيذ الصحيح — مفتاح يديره العميل مع تدوير تلقائي:
# Step 1: Create a customer-managed KMS key
KEY_ID=$(aws kms create-key \
--origin AWS_KMS \
--description "Customer-managed key for production PII — Article 32 compliant" \
--tags TagKey=Purpose,TagValue=GDPR TagKey=Environment,TagValue=production \
--query 'KeyMetadata.KeyId' \
--output text)
echo "Created KMS key: $KEY_ID"
# Step 2: Enable automatic 90-day rotation
aws kms enable-key-rotation --key-id $KEY_ID
# Step 3: Apply to your production RDS instance
aws rds modify-db-instance \
--db-instance-identifier production-db \
--kms-key-id $KEY_ID \
--apply-immediately
سؤال المدقق: "أرني أن مفاتيح التشفير الخاصة بك يتم تدويرها تلقائياً وأنك تستطيع إثبات من قام بالوصول إليها."
دليلك:
# Verify rotation is enabled — expected output: true
aws kms get-key-rotation-status --key-id $KEY_ID \
--query 'KeyRotationEnabled'
# Show the CloudTrail audit trail of every key usage event
aws logs filter-log-events \
--log-group-name cloudtrail-logs \
--filter-pattern '{ $.eventSource = "kms.amazonaws.com" }' \
--query 'events[*].{Time:timestamp,Event:message}' \
--output table
2.3. كيفية تطبيق التشفير على مستوى التطبيق (Application-Layer Encryption) للحقول الحساسة
تشفير التخزين هو الحد الأدنى. التشفير على مستوى التطبيق هو الحد الأقصى الذي يتوقعه مدققو المادة 32 بشكل متزايد للبيانات الصحية والسجلات المالية وغيرها من البيانات الشخصية الحساسة. إليك الفرق: مع تشفير التخزين فقط، يرى مسؤول قاعدة البيانات الذي يقوم بتشغيل SELECT email FROM users عنوان البريد الإلكتروني بنص واضح (plaintext). مع التشفير على مستوى التطبيق، يرى gAAAAABm... — سلسلة بايت مشفرة لا يمكن للتطبيق (مع الوصول إلى مفتاح Vault) فك تشفيرها إلا هو.
# application_encryption.py
from cryptography.fernet import Fernet
class FieldEncryption:
"""
Encrypts sensitive personal data fields before they are stored in the database.
The encryption key is stored in HashiCorp Vault or AWS Secrets Manager — never in code.
A database administrator with direct SQL access sees only encrypted bytes.
"""
def __init__(self, key: str):
# key must be a 32-byte base64-encoded string — retrieve from Vault
self.cipher = Fernet(key.encode())
def encrypt_field(self, plaintext: str) -> str:
"""Encrypt a sensitive field before writing to the database."""
if not plaintext:
return None
encrypted_bytes = self.cipher.encrypt(plaintext.encode())
return encrypted_bytes.decode()
def decrypt_field(self, ciphertext: str) -> str:
"""
Decrypt a field when legitimately needed by the application.
This method requires the Vault key — database admins cannot call it.
"""
if not ciphertext:
return None
decrypted_bytes = self.cipher.decrypt(ciphertext.encode())
return decrypted_bytes.decode()
# Usage in your application:
from vault_client import get_secret # Your Vault or Secrets Manager client
# Retrieve the encryption key at application startup — never hardcode it
encryption_key = get_secret("gdpr/field-encryption-key")
encryptor = FieldEncryption(encryption_key)
# Before storing a user's health record
user.health_data_encrypted = encryptor.encrypt_field(user.health_data_plaintext)
# Before reading for a legitimate purpose (subject access request, etc.)
health_data = encryptor.decrypt_field(user.health_data_encrypted)
سؤال المدقق: "إذا قام مسؤول قاعدة بيانات بالاستعلام عن جدول users مباشرة، هل يمكنه قراءة بيانات صحة العميل بنص واضح (plaintext)؟"
دليلك: قم بتشغيل استعلام قاعدة بيانات مباشر واعرض للمدقق الإخراج المشفر. ثم أظهر أن مفتاح فك التشفير لا يمكن الوصول إليه من قبل مسؤولي قاعدة البيانات — يتم استرداده فقط بواسطة التطبيق من خلال Vault.
الجزء الثالث: المادة 32(1)(ب) — السرية (Confidentiality) والنزاهة (Integrity)
3.1. كيفية تطبيق تسجيل الخروج التلقائي (Automatic Logoff)
تتطلب المادة 32(1)(ب) الحماية ضد "الوصول غير المصرح به إلى البيانات الشخصية". الجلسة التي لا تنتهي أبداً — أو تنتهي بعد 24 ساعة — هي فجوة في التحكم بالوصول. المستخدم الذي يسجل الدخول على جهاز مشترك ويغادر قد ترك باباً مفتوحاً.
إليك النهج غير الصحيح — جلسة JWT مدتها 24 ساعة:
// Bad: 24-hour access token with no inactivity check
const token = jwt.sign(
{ userId: user.id, role: user.role },
process.env.JWT_SECRET,
{ expiresIn: '24h' } // Too long — violates Article 32 intent
);
المشكلة: إذا قام مستخدم بتسجيل الدخول على جهاز كمبيوتر مشترك وأغلق الجهاز المحمول دون تسجيل الخروج، تظل الجلسة صالحة لمدة تصل إلى 24 ساعة. يمكن لأي شخص يفتح هذا الجهاز المحمول الوصول إلى البيانات الشخصية.
إليك التنفيذ الصحيح — رمز وصول (access token) مدته 15 دقيقة مع تحديث متجدد (rolling refresh):
// Good: Short-lived access token with rolling refresh via HTTP-only cookie
// Access token — valid for 15 minutes of activity
const accessToken = jwt.sign(
{ userId: user.id, role: user.role, type: 'access' },
process.env.JWT_ACCESS_SECRET,
{ expiresIn: '15m' }
);
// Refresh token — valid for 8 hours total session duration
const refreshToken = jwt.sign(
{ userId: user.id, type: 'refresh' },
process.env.JWT_REFRESH_SECRET,
{ expiresIn: '8h' }
);
// Set refresh token as HTTP-only cookie — not accessible to JavaScript
res.cookie('refreshToken', refreshToken, {
httpOnly: true, // Prevents XSS access
secure: true, // HTTPS only
sameSite: 'strict', // Prevents CSRF
maxAge: 8 * 60 * 60 * 1000 // 8 hours in milliseconds
});
// Session middleware that enforces absolute timeout
const MAX_TOTAL_SESSION_MS = 8 * 60 * 60 * 1000; // 8 hours
app.use((req, res, next) => {
if (!req.session?.createdAt) return next();
const sessionAge = Date.now() - req.session.createdAt;
if (sessionAge > MAX_TOTAL_SESSION_MS) {
req.session.destroy();
return res.status(401).json({ error: 'Session expired after 8 hours. Please log in again.' });
}
next();
});
سؤال المدقق: "أرني أن تطبيقك ينهي الجلسات غير النشطة بعد فترة معقولة."
دليلك: لقطة شاشة لأدوات مطور المتصفح (browser developer tools) تظهر وقت انتهاء صلاحية ملف تعريف الارتباط (cookie)، بالإضافة إلى تسجيل اختبار يوضح أنه بعد 15 دقيقة من عدم النشاط، يتم عرض مطالبة بإعادة المصادقة للمستخدم.
3.2. كيفية تطبيق تحديد هوية المستخدم الفريدة (Unique User Identification) باستخدام IRSA
تتطلب المادة 32(1)(ب) أن تتمكن من تحديد من قام بالوصول إلى البيانات الشخصية. حسابات الخدمة المشتركة (Shared service accounts) تجعل هذا مستحيلاً — يظهر سجل التدقيق data-export-service ولكن لا يمكنك معرفة أي مهندس قام بتشغيل التصدير.
إليك النهج غير الصحيح — حساب خدمة مشترك:
# Bad: One shared Kubernetes service account used by multiple engineers and pipelines
apiVersion: v1
kind: ServiceAccount
metadata:
name: data-export # Three engineers and two pipelines share this identity
namespace: production
عندما يظهر سجل تدقيق أن data-export قام بتصدير جماعي للمستخدمين في الساعة 03:17 بالتوقيت العالمي المنسق (UTC)، لا يمكنك الإجابة على سؤال المدقق: "من قام بالتصريح بذلك؟"
إليك التنفيذ الصحيح — أدوار IAM لحسابات الخدمة (IRSA):
# Step 1: Create a separate IAM role for each service identity
# This command creates a role that can only be assumed by the 'payment-service'
# Kubernetes service account in the 'production' namespace
aws iam create-role \
--role-name eks-payment-service-role \
--assume-role-policy-document '{ "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Principal": { "Federated": "arn:aws:iam::123456789012:oidc-provider/oidc.eks.us-east-1.amazonaws.com/id/YOUR_OIDC_ID" }, "Action": "sts:AssumeRoleWithWebIdentity", "Condition": { "StringEquals": { "oidc.eks.us-east-1.amazonaws.com/id/YOUR_OIDC_ID:sub": "system:serviceaccount:production:payment-service" } } }] }'
# Step 2: Annotate the Kubernetes service account with its unique IAM role
apiVersion: v1
kind: ServiceAccount
metadata:
name: payment-service # One service account, one service, one role
namespace: production
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/eks-payment-service-role
كل استدعاء لـ AWS API من payment-service يظهر الآن في CloudTrail باسم eks-payment-service-role — هوية فريدة يمكن تتبعها. لا توجد حسابات مشتركة. لا توجد سجلات تدقيق غامضة.
سؤال المدقق: "كيف تضمن أن كل إجراء على البيانات الشخصية يمكن نسبته إلى فرد أو خدمة محددة؟"
دليلك:
# Verify no shared service accounts exist — every account should have a unique role annotation
kubectl get serviceaccounts --all-namespaces \
-o jsonpath='{range .items[*]}{.metadata.namespace}/{.metadata.name}: {.metadata.annotations.eks\.amazonaws\.com/role-arn}{"\n"}{end}'
الجزء الرابع: المادة 32(1)(ج) — التوفر (Availability) والمرونة (Resilience)
4.1. كيفية تطبيق متطلبات Multi-AZ والنسخ الاحتياطي
تتطلب المادة 32(1)(ج) "القدرة على استعادة توفر البيانات الشخصية والوصول إليها في الوقت المناسب في حالة وقوع حادث مادي أو تقني". هذا ليس اقتراحاً — إنه مطلب قانوني. إذا كانت قاعدة بياناتك في منطقة توفر واحدة (single Availability Zone) وشهدت تلك المنطقة حدث شبكة، فأنت في حالة انتهاك.
إليك النهج غير الصحيح — RDS في منطقة توفر واحدة بدون نسخ احتياطية مؤتمتة:
# Bad: Single-AZ RDS — one networking event makes personal data unavailable
resource "aws_db_instance" "production" {
identifier = "production-database"
multi_az = false # No automatic failover
backup_retention_period = 0 # No automated backups — Article 32 violation
}
إذا واجهت منطقة التوفر مشكلة في الشبكة، تصبح قاعدة البيانات غير قابلة للوصول. إذا تم إتلاف المثيل، فلا توجد نسخ احتياطية للاستعادة. كلا السيناريوهين ينتهكان المادة 32(1)(ج).
إليك التنفيذ الصحيح — Multi-AZ مع نسخ احتياطية مؤتمتة تم اختبارها:
# Good: Multi-AZ RDS with 30-day backup retention
resource "aws_db_instance" "production" {
identifier = "production-database"
# Multi-AZ creates a synchronous standby replica in a different AZ
# Automatic failover completes in 60-120 seconds with no data loss
multi_az = true
# 30-day backup retention — gives you recovery point flexibility
backup_retention_period = 30
backup_window = "03:00-04:00" # Low-traffic window for backup
# Copy all tags to snapshots for compliance tracking
copy_tags_to_snapshot = true
# Performance Insights for monitoring query health
performance_insights_enabled = true
performance_insights_retention_period = 7
tags = {
Environment = "production"
DataClassification = "personal-data"
GDPRScope = "article32"
}
}
كيفية اختبار RTO و RPO شهرياً:
# Step 1: Find your most recent automated snapshot
SNAPSHOT_ID=$(aws rds describe-db-snapshots \
--db-instance-identifier production-database \
--snapshot-type automated \
--query 'sort_by(DBSnapshots, &SnapshotCreateTime)[-1].DBSnapshotIdentifier' \
--output text)
echo "Testing restore of snapshot: $SNAPSHOT_ID"
# Step 2: Start the restore — measure the time
START_TIME=$(date +%s)
aws rds restore-db-instance-from-db-snapshot \
--db-instance-identifier gdpr-restore-test \
--db-snapshot-identifier $SNAPSHOT_ID \
--db-instance-class db.t3.medium \
--no-publicly-accessible \
--tags Key=Purpose,Value=gdpr-rto-test Key=DeleteAfter,Value=$(date -d '+1 day' +%Y-%m-%d)
# Step 3: Wait for restore to complete
aws rds wait db-instance-available \
--db-instance-identifier gdpr-restore-test
END_TIME=$(date +%s)
RTO_SECONDS=$((END_TIME - START_TIME))
echo "Restore completed in $((RTO_SECONDS / 60)) minutes"
# Step 4: Verify data integrity with a spot check
# Connect to the restored instance and verify record counts match production
# psql -h RESTORED_ENDPOINT -U admin -d production \
# -c "SELECT COUNT(*) FROM users; SELECT MAX(created_at) FROM orders;"
# Step 5: Delete the test instance
aws rds delete-db-instance \
--db-instance-identifier gdpr-restore-test \
--skip-final-snapshot
سؤال المدقق: "ما هو هدف وقت الاستعادة (Recovery Time Objective - RTO) وهدف نقطة الاستعادة (Recovery Point Objective - RPO) للبيانات الشخصية؟ ومتى قمت باختباره آخر مرة؟"
دليلك: سجل اختبار DR شهري موثق يوضح: اللقطة المستخدمة، وقت بدء الاستعادة، وقت اكتمال الاستعادة، نتائج استعلام التحقق من البيانات، والمهندس الذي أجرى الاختبار.
الجزء الخامس: المادة 32(1)(د) — الاختبار المنتظم
5.1. كيفية تطبيق فحص الثغرات الأمنية المؤتمت (Automated Vulnerability Scanning)
تتطلب المادة 32(1)(د) "عملية لاختبار وتقييم فعالية التدابير التقنية والتنظيمية بانتظام". يشمل ذلك فحص الثغرات الأمنية المؤتمت لكل صورة حاوية (container image) قبل وصولها إلى الإنتاج.
إليك النهج غير الصحيح — لا يوجد فحص في مسار النشر (deployment pipeline):
# Bad: No vulnerability scanning — a critical CVE in the base image deploys undetected
name: Deploy
on: [push]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: docker build -t myapp .
- run: docker push myapp # Deploys without any security check
إذا كانت هناك ثغرة CVE حرجة موجودة في الصورة الأساسية (مثل ثغرة تنفيذ كود عن بعد في OpenSSL)، فإنها تنتقل مباشرة إلى الإنتاج. بموجب المادة 32(1)(د)، يعتبر هذا اكتشافاً.
إليك التنفيذ الصحيح — فحص Trivy مع فرض في مسار العمل (pipeline enforcement):
# Good: Trivy scans every image — CRITICAL/HIGH CVEs block the deployment
name: Security Scan and Deploy
on: [push, pull_request]
jobs:
trivy-scan:
name: Container Vulnerability Scan
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build container image
run: docker build -t myapp:${{ github.sha }} .
- name: Scan for vulnerabilities with Trivy
uses: aquasecurity/trivy-action@master
with:
image-ref: 'myapp:${{ github.sha }}'
format: 'sarif'
output: 'trivy-results.sarif'
severity: 'CRITICAL,HIGH'
exit-code: '1' # Fail the pipeline — image cannot deploy with CRITICAL/HIGH CVEs
- name: Upload scan results to GitHub Security tab
uses: github/codeql-action/upload-sarif@v2
if: always() # Upload results even if scan failed, for review
with:
sarif_file: 'trivy-results.sarif'
يقوم Trivy بالبحث عن:
- ثغرات CVE في الصورة الأساسية.
- حزم نظام التشغيل (على سبيل المثال، ثغرة OpenSSL حرجة في قاعدة Ubuntu الخاصة بك).
- إصدارات ضعيفة من تبعيات التطبيق (استغلال معروف في حزمة npm أو pip يستخدمها تطبيقك).
- تكوينات خاطئة في Dockerfile (التشغيل كـ root، استخدام علامة
latestبدلاً من SHA مثبت).
تظهر النتائج في علامة تبويب GitHub Security، مما ينشئ سجلاً مؤرخاً وقابلاً للبحث لكل فحص. هذا السجل هو دليلك للمادة 32(1)(د).
كيفية تشغيل تقييم AWS Inspector أسبوعياً لأحمال العمل الجارية:
# List all active CRITICAL findings across your AWS account
aws inspector2 list-findings \
--filter-criteria '{ "severity": [{"comparison": "EQUALS", "value": "CRITICAL"}], "findingStatus": [{"comparison": "EQUALS", "value": "ACTIVE"}] }' \
--query 'findings[*].{ Title:title, Resource:resources[0].id, Severity:severity, CVE:packageVulnerabilityDetails.vulnerabilityId }' \
--output table
سؤال المدقق: "أرني برنامج إدارة الثغرات الأمنية الخاص بك، بما في ذلك كيفية تحديد أولويات الاكتشافات ومعالجتها."
دليلك: تقرير أسبوعي عن الثغرات الأمنية — يتم إنشاؤه تلقائياً من الأمر أعلاه — يوضح الاكتشافات النشطة، الخطورة، مشكلة GitHub التي تم إنشاؤها لكل اكتشاف، وتاريخ الإغلاق بمجرد معالجتها.
الجزء السادس: المادة 32(1)(د) — اختبار الاختراق (Penetration Testing)
6.1. لماذا لا يكفي الفحص المؤتمت (Automated Scanning)
تتطلب المادة 32(1)(د) تقييم فعالية تدابير الأمان. تجد الماسحات الضوئية المؤتمتة للثغرات الأمنية ثغرات CVE المعروفة في المكتبات وحزم نظام التشغيل. لا يمكنها العثور على:
- ثغرات منطق الأعمال (Business logic vulnerabilities) (نقطة نهاية API تعيد بيانات مستخدم آخر عند إعطائها معلمة محددة).
- تجاوزات المصادقة (Authentication bypasses) (تطبيق JWT يقبل الرموز غير الموقعة).
- مسارات تصعيد الامتيازات (Privilege escalation paths) (يمكن للمهاجم الانتقال من دور ذي امتيازات منخفضة إلى مسؤول من خلال سلسلة من استدعاءات API المشروعة).
- مراجع الكائنات المباشرة غير الآمنة (Insecure direct object references) (الوصول إلى
/api/users/124بدلاً من/api/users/123يعيد بيانات لعميل مختلف).
تذكر كل من ICO (مكتب مفوض المعلومات في المملكة المتحدة) و CNIL (هيئة حماية البيانات الفرنسية) في إرشاداتهما أن اختبار الاختراق اليدوي السنوي متوقع للمؤسسات التي تعالج كميات كبيرة من البيانات الشخصية.
كيف يبدو نطاق اختبار الاختراق المقبول:
# Annual Penetration Test Scope — Article 32 Compliance
## Testing Period
Start: 2025-04-01
End: 2025-04-14
Testing firm: [Accredited firm — CREST or CHECK certified]
## In Scope
- Production web application: https://app.yourcompany.com
- Production API: https://api.yourcompany.com/v1/*
- Authentication flows: OAuth2, JWT, session management
- Data stores: PostgreSQL (via application access only, not direct DB access)
- AWS account: External reconnaissance of public-facing services only
## Testing Types
- External infrastructure testing (all public IP ranges)
- Web application testing (OWASP Top 10 2021)
- API security testing (all authenticated and unauthenticated endpoints)
- Authentication and session management testing
- GDPR-specific test cases (data subject rights endpoints, consent flows)
## Remediation SLAs
- CRITICAL: 24 hours from report delivery
- HIGH: 7 calendar days
- MEDIUM: 30 calendar days
- LOW: 90 calendar days
كيفية تتبع وإثبات المعالجة:
# Create GitHub issues for each finding on receipt of the pen test report
# This creates a traceable record of every finding and its resolution
for finding_id in $(cat pentest-report-findings.txt); do
gh issue create \
--title "Pen test finding: $finding_id" \
--body "See pentest-report-2025-04.pdf, section $finding_id. Severity: HIGH. SLA: 7 days." \
--label "security,pentest" \
--assignee "@security-lead"
done
سؤال المدقق: "متى كان آخر اختبار اختراق لك؟ أرني التقرير ودليلك على المعالجة."
دليلك:
- تقرير اختبار الاختراق من شركة معتمدة من CREST أو CHECK، مؤرخ خلال الـ 12 شهراً الماضية.
- متتبع معالجة (GitHub issues أو Jira) يوضح كل اكتشاف CRITICAL و HIGH مع تاريخ الإغلاق.
- دليل على إغلاق جميع الاكتشافات CRITICAL في غضون 24 ساعة (سجل git commit أو النشر).
أفضل الممارسات للامتثال للمادة 32 من GDPR
إليك النقاط الرئيسية المستخلصة من هذا الدليل:
- ✅ افعل: طبق التشفير على مستوى التطبيق (application-layer encryption) للحقول الحساسة. تشفير التخزين وحده لا يكفي — لا يزال بإمكان مسؤول قاعدة البيانات الذي لديه وصول مباشر إلى قاعدة البيانات قراءة النص الواضح (plaintext).
- ✅ افعل: استخدم مفاتيح KMS المدارة من قبل العميل (customer-managed KMS keys) مع التدوير التلقائي. تحتاج إلى إثبات التحكم في مادة المفتاح.
- ✅ افعل: قم بتخزين البيانات المخفية الهوية المستعار (pseudonymised data) بشكل منفصل عن المعرفات، مع وصول مقيد ومستند إلى الأدوار (role-based access) إلى جدول البحث.
- ✅ افعل: فرض تسجيل الخروج التلقائي (automatic logoff) بعد 15 دقيقة من عدم النشاط مع حد مطلق للجلسة مدته 8 ساعات.
- ✅ افعل: استخدم حسابات خدمة فريدة مع IRSA. يجب أن يكون كل إجراء على البيانات الشخصية قابلاً للنسبة إلى هوية محددة.
- ✅ افعل: اختبر نسخك الاحتياطية شهرياً. وثّق RTO و RPO بنتائج اختبار الاستعادة الفعلية.
- ✅ افعل: قم بتشغيل Trivy في CI لحظر ثغرات CVE من فئة CRITICAL و HIGH قبل النشر.
- ✅ افعل: قم بإجراء اختبار اختراق يدوي سنوي من شركة معتمدة من CREST أو CHECK.
- ❌ لا تفعل: استخدام جلسات JWT لمدة 24 ساعة أو جلسات بدون مهلة عدم نشاط.
- ❌ لا تفعل: تخزين الأسرار في متغيرات البيئة (environment variables)، ملفات .env، أو ترميزها بشكل ثابت في الكود المصدري.
- ❌ لا تفعل: تخطي اختبار الاختراق السنوي. لن يقبل المدقق من ICO أو CNIL "نقوم بتشغيل فحوصات مؤتمتة" كبديل.
- ❌ لا تفعل: استخدام مفاتيح KMS المدارة من قبل AWS إذا كنت بحاجة إلى إثبات التحكم في مادة المفتاح للمدقق الخاص بك.
المصادر
- ICO Guide to GDPR Article 32 — الإرشادات الرسمية لمكتب مفوض المعلومات في المملكة المتحدة بشأن التزامات أمان المادة 32.
- ENISA Guidelines on Article 32 — إرشادات وكالة الاتحاد الأوروبي للأمن السيبراني للشركات الصغيرة والمتوسطة بشأن أمان البيانات الشخصية.
- Trivy by Aqua Security — ماسح ضوئي مفتوح المصدر لثغرات الحاويات يستخدم في الجزء الخامس.
- OWASP Top 10 2021 — المرجع القياسي لمخاطر أمان تطبيقات الويب، المستخدم في تحديد نطاق اختبار الاختراق.
- AWS KMS Key Rotation Documentation — وثائق AWS الرسمية لتدوير المفاتيح تلقائياً.
- PostgreSQL Row Security Policies — كيفية تطبيق أمان على مستوى الصف (row-level security) للتحكم الدقيق في الوصول إلى البيانات المخفية الهوية المستعار.
- EKS IAM Roles for Service Accounts (IRSA) — وثائق AWS الرسمية لهوية حساب الخدمة الفريدة على EKS.
- CREST Certified Testing Firms — دليل شركات اختبار الاختراق المعتمدة من CREST لتقييم المادة 32 السنوي الخاص بك.