نوفيل
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
دخولابدأ مجاناً
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
نوفيل

منصة تعليم البرمجة الأولى بالعربي. تعلم من الصفر حتى الاحتراف مع كورسات احترافية وتحديات ذكاء اصطناعي.

المنصة

  • الكورسات
  • التحديات
  • المقالات
  • الأدوات

الحساب

  • إنشاء حساب
  • تسجيل الدخول
  • لوحة التحكم
  • الملف الشخصي

روابط

  • سياسة الخصوصية
  • شروط الاستخدام
  • عن نوفيل
  • تواصل معنا

© 2026 نوفيل. جميع الحقوق محفوظة.

صُنع بـ في مصر

نوفيل
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
دخولابدأ مجاناً
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
نوفيل

منصة تعليم البرمجة الأولى بالعربي. تعلم من الصفر حتى الاحتراف مع كورسات احترافية وتحديات ذكاء اصطناعي.

المنصة

  • الكورسات
  • التحديات
  • المقالات
  • الأدوات

الحساب

  • إنشاء حساب
  • تسجيل الدخول
  • لوحة التحكم
  • الملف الشخصي

روابط

  • سياسة الخصوصية
  • شروط الاستخدام
  • عن نوفيل
  • تواصل معنا

© 2026 نوفيل. جميع الحقوق محفوظة.

صُنع بـ في مصر

نوفيل
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
دخولابدأ مجاناً
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
نوفيل

منصة تعليم البرمجة الأولى بالعربي. تعلم من الصفر حتى الاحتراف مع كورسات احترافية وتحديات ذكاء اصطناعي.

المنصة

  • الكورسات
  • التحديات
  • المقالات
  • الأدوات

الحساب

  • إنشاء حساب
  • تسجيل الدخول
  • لوحة التحكم
  • الملف الشخصي

روابط

  • سياسة الخصوصية
  • شروط الاستخدام
  • عن نوفيل
  • تواصل معنا

© 2026 نوفيل. جميع الحقوق محفوظة.

صُنع بـ في مصر

نوفيل
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
دخولابدأ مجاناً
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
المقالات/الأمن السيبراني
الأمن السيبراني

كيف تؤمن API الخاصة بك: من Authentication إلى Rate Limiting دون أن يفلت منك شيء

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

فريق نوفيل١١ أغسطس ٢٠٢٦10 دقائق قراءة١٢ مشاهدة

في عام 2021، تعرضت شركة LinkedIn لهجوم أدى إلى تسريب بيانات 700 مليون مستخدم. السبب؟ API واحد غير محمي بشكل كافٍ. لم يكن الخطأ في الكود نفسه، بل في افتراض أن المصادقة الأساسية كافية. الحقيقة هي أن تأمين API ليس مجرد خطوة في قائمة المهام، بل هو عملية مستمرة تبدأ من اللحظة التي تكتب فيها أول سطر كود وتنتهي عندما توقف الخدمة نهائياً. المشكلة الأكبر أن معظم المطورين يظنون أنهم فعلوا كل شيء صحيح بمجرد إضافة JWT أو مفتاح API، بينما الهجمات الحقيقية تأتي من زوايا غير متوقعة: مثل الـ Replay Attacks أو الـ Side-Channel Leaks.

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

1. المصادقة: لماذا JWT ليس الحل السحري

الكثير من المطورين يستخدمون JWT كحل سحري للمصادقة، لكنهم ينسون أن JWT ليس آمناً بذاته. الأمان يأتي من كيفية استخدامه. مثلاً، إذا استخدمت خوارزمية HS256 مع مفتاح ضعيف، يمكن للمهاجم فك الـ Signature بسهولة. حتى لو استخدمت RS256، إذا لم تقم بتفعيل الـ JWKS والتحقق من الـ Kid، يمكن للمهاجم استبدال الـ Token بآخر مزيف. المشكلة الأكبر أن معظم المكتبات الجاهزة مثل jsonwebtoken في Node.js لا تفعل هذه الفحوصات افتراضياً، مما يترك ثغرة مفتوحة دون أن تدري.

لنأخذ مثالاً عملياً: في عام 2018، اكتشفت شركة Tesla ثغرة في واجهة برمجة التطبيقات الخاصة بها تسمح لأي شخص بالتحكم في السيارات عن بعد. السبب؟ استخدام JWT بدون التحقق من الـ Audience Claim. المهاجمون استطاعوا توليد توكنات صالحة باستخدام مفتاح عام متاح للجميع. الدرس المستفاد: لا تعتمد على JWT وحده، ولا تستخدم مكتبات جاهزة بدون فهم كامل لما تفعله خلف الكواليس.

javascript
// مثال على تحقق آمن من JWT باستخدام Node.js
const jwt = require('jsonwebtoken');
const jwksClient = require('jwks-rsa');

const client = jwksClient({
 jwksUri: 'https://your-auth-server/.well-known/jwks.json'
});

function getKey(header, callback) {
 client.getSigningKey(header.kid, (err, key) => {
 const signingKey = key.getPublicKey();
 callback(null, signingKey);
 });
}

async function verifyToken(token) {
 try {
 const decoded = await jwt.verify(token, getKey, {
 algorithms: ['RS256'],
 audience: 'your-api-client-id', // تحقق من Audience
 issuer: 'https://your-auth-server', // تحقق من Issuer
 clockTolerance: 5 // سماح بخطأ توقيت بسيط
 });
 return decoded;
 } catch (err) {
 throw new Error('Invalid token');
 }
}

// لا تنسَ إضافة Rate Limiting هنا أيضاً!

لاحظ أننا لم نستخدم المفتاح السري مباشرة في الكود، بل اعتمدنا على JWKS للحصول على المفتاح العام. هذا يمنع المهاجم من استخدام مفتاح مزيف. أيضاً، قمنا بتفعيل التحقق من الـ Audience والـ Issuer، وهما عنصران أساسيان غالباً ما يتم تجاهلهما. لكن حتى مع كل هذه الإجراءات، لا يزال هناك خطر: ماذا لو سرق المهاجم التوكن نفسه؟ هنا يأتي دور الـ Token Revocation والـ Short-Lived Tokens.

الـ Short-Lived Tokens والـ Refresh Tokens: التوازن بين الأمان والراحة

الـ JWT العادي يمكن أن يعيش لساعات أو أيام، مما يعني أنه إذا سرقه مهاجم، يمكنه استخدامه طوال هذه الفترة. الحل هو استخدام توكنات قصيرة العمر (مثل 15 دقيقة) مع Refresh Token طويل العمر. لكن هنا تكمن المشكلة: إذا لم يتم تخزين الـ Refresh Token بشكل آمن، يصبح نقطة ضعف جديدة. مثلاً، في عام 2020، تعرضت شركة Twitter لهجوم بسبب تخزين الـ Refresh Tokens في قاعدة بيانات غير مشفرة. المهاجمون استطاعوا الوصول إلى حسابات حساسة باستخدام هذه التوكنات.

الحل الأمثل هو تخزين الـ Refresh Token في HttpOnly وSecure Cookies، مع تفعيل الـ SameSite Attribute لمنع هجمات CSRF. أيضاً، يجب أن يكون الـ Refresh Token قابلاً للإلغاء في أي وقت، ويجب أن يتم التحقق منه في كل طلب. إليك مثال على كيفية تنفيذ ذلك في Node.js:

javascript
// تنفيذ Refresh Token مع HttpOnly Cookie
const express = require('express');
const cookieParser = require('cookie-parser');
const crypto = require('crypto');

const app = express();
app.use(cookieParser());

const refreshTokens = new Set(); // في الواقع، استخدم قاعدة بيانات

app.post('/login', (req, res) => {
 // بعد التحقق من بيانات المستخدم
 const accessToken = generateAccessToken();
 const refreshToken = crypto.randomBytes(64).toString('hex');
 
 refreshTokens.add(refreshToken);
 
 res.cookie('refreshToken', refreshToken, {
 httpOnly: true,
 secure: true, // فقط عبر HTTPS
 sameSite: 'strict', // منع CSRF
 maxAge: 7 * 24 * 60 * 60 * 1000 // أسبوع
 });
 
 res.json({ accessToken });
});

app.post('/refresh', (req, res) => {
 const refreshToken = req.cookies.refreshToken;
 if (!refreshToken || !refreshTokens.has(refreshToken)) {
 return res.status(403).json({ error: 'Invalid refresh token' });
 }
 
 // تحقق من أن التوكن لم يُلغى
 const newAccessToken = generateAccessToken();
 res.json({ accessToken: newAccessToken });
});

app.post('/logout', (req, res) => {
 const refreshToken = req.cookies.refreshToken;
 refreshTokens.delete(refreshToken);
 res.clearCookie('refreshToken');
 res.json({ message: 'Logged out' });
});

2. التفويض: لا تكتفِ بـ 'user' و 'admin'

الكثير من المطورين يعتقدون أن التفويض هو مجرد التحقق من دور المستخدم (role). لكن في الواقع، التفويض هو عملية معقدة تتطلب التحقق من السياق الكامل للطلب. مثلاً، قد يكون المستخدم مديراً، لكن هل لديه الصلاحية لتعديل هذا السجل المحدد؟ هل هذا الطلب يأتي من IP موثوق؟ هل الوقت مناسب لتنفيذ هذه العملية؟ في عام 2019، تعرضت شركة Capital One لاختراق أدى إلى تسريب بيانات 100 مليون مستخدم. السبب؟ عدم التحقق من السياق في واجهة برمجة التطبيقات الخاصة بهم، مما سمح للمهاجم بالوصول إلى بيانات حساسة رغم أنه كان لديه صلاحيات محدودة.

الحل هو استخدام نموذج تفويض قائم على السياق، مثل الـ Attribute-Based Access Control (ABAC). بدلاً من التحقق من دور المستخدم فقط، تحقق من مجموعة من السمات مثل: الدور، الوقت، الموقع الجغرافي، نوع الجهاز، وحتى سلوك المستخدم. مثلاً، إذا كان المستخدم يحاول الوصول إلى البيانات من بلد غير معتاد، يمكنك طلب مصادقة إضافية. إليك مثال على كيفية تنفيذ ذلك باستخدام مكتبة Casbin في Node.js:

javascript
// تنفيذ ABAC باستخدام Casbin
const { newEnforcer } = require('casbin');

async function initCasbin() {
 const enforcer = await newEnforcer('model.conf', 'policy.csv');
 return enforcer;
}

async function checkPermission(user, resource, action, context) {
 const enforcer = await initCasbin();
 // السياق يمكن أن يشمل الموقع، الوقت، الجهاز، إلخ.
 const allowed = await enforcer.enforce(
 user.role,
 `${resource}:${context.country}:${context.time}`,
 action
 );
 return allowed;
}

// مثال على policy.csv
// p, admin, document:US:day, read
// p, admin, document:*:night, deny
// p, user, document:US:*, read
// p, user, document:*:*, deny

في هذا المثال، لا يعتمد التفويض على الدور فقط، بل أيضاً على الموقع الجغرافي (US) والوقت (day أو night). هذا يجعل النظام أكثر أماناً ومرونة. لكن حتى مع هذا النموذج، لا يزال هناك خطر: ماذا لو حاول المهاجم تجاوز هذه الفحوصات باستخدام هجمات مثل الـ Parameter Tampering؟ هنا يأتي دور التحقق من المدخلات.


3. حماية المدخلات: لا تثق بأي شيء يأتي من العميل

أحد أكبر الأخطاء التي يقع فيها المطورون هو الثقة بالبيانات القادمة من العميل. حتى لو استخدمت أفضل أنظمة المصادقة والتفويض، إذا لم تتحقق من المدخلات، يمكن للمهاجم استغلال ذلك لإدخال أكواد خبيثة أو تجاوز الفحوصات. مثلاً، في عام 2017، تعرضت شركة Equifax لاختراق أدى إلى تسريب بيانات 143 مليون مستخدم. السبب؟ عدم التحقق من مدخلات واجهة برمجة التطبيقات الخاصة بهم، مما سمح للمهاجمين بتنفيذ هجمات SQL Injection.

الحل هو استخدام مكتبات متخصصة للتحقق من المدخلات، مثل Joi في Node.js أو Pydantic في Python. لكن لا تعتمد على التحقق من جانب العميل فقط، بل يجب أن يكون التحقق مزدوجاً: في العميل والسيرفر. أيضاً، استخدم تقنيات مثل الـ Prepared Statements لمنع هجمات SQL Injection، و الـ Content Security Policy لمنع هجمات XSS. إليك مثال على كيفية التحقق من المدخلات باستخدام Joi:

javascript
const Joi = require('joi');

const userSchema = Joi.object({
 username: Joi.string()
 .alphanum()
 .min(3)
 .max(30)
 .required(),
 
 email: Joi.string()
 .email()
 .required(),
 
 password: Joi.string()
 .pattern(new RegExp('^[a-zA-Z0-9]{8,30}$'))
 .required(),
 
 role: Joi.string()
 .valid('user', 'admin')
 .default('user'),
 
 age: Joi.number()
 .integer()
 .min(18)
 .max(120),
 
 metadata: Joi.object().pattern(
 Joi.string(),
 Joi.alternatives().try(
 Joi.string(),
 Joi.number(),
 Joi.boolean()
 )
 ).max(10) // حد أقصى 10 مفاتيح
});

async function validateUser(user) {
 try {
 const value = await userSchema.validateAsync(user);
 return value;
 } catch (err) {
 throw new Error(`Validation error: ${err.details[0].message}`);
 }
}

لاحظ أننا لم نكتفِ بالتحقق من النوع، بل أيضاً من النمط (pattern) للقيم. مثلاً، كلمة المرور يجب أن تحتوي على أحرف وأرقام فقط، والـ metadata يجب ألا يحتوي على أكثر من 10 مفاتيح. لكن حتى مع هذا التحقق، لا يزال هناك خطر: ماذا لو أرسل المهاجم طلبات متكررة لإسقاط السيرفر؟ هنا يأتي دور الـ Rate Limiting.


4. Rate Limiting: كيف تمنع الهجمات دون أن تزعج المستخدمين الحقيقيين

الـ Rate Limiting هو أحد أهم طبقات الأمان التي يتم تجاهلها. بدونها، يمكن للمهاجم إرسال آلاف الطلبات في الثانية لإسقاط السيرفر أو استنزاف الموارد. مثلاً، في عام 2016، تعرض موقع KrebsOnSecurity لهجوم DDoS بقوة 620 جيجابت في الثانية. السبب؟ عدم وجود Rate Limiting على واجهة برمجة التطبيقات الخاصة بهم، مما سمح للمهاجمين بإرسال ملايين الطلبات في وقت قصير.

لكن المشكلة في الـ Rate Limiting هي أنه يمكن أن يؤثر على المستخدمين الحقيقيين إذا لم يتم تنفيذه بشكل ذكي. مثلاً، إذا وضعت حداً ثابتاً (مثل 100 طلب في الدقيقة)، قد يتأثر المستخدمون الذين يستخدمون التطبيق بشكل مكثف. الحل هو استخدام خوارزميات ذكية مثل الـ Token Bucket أو الـ Leaky Bucket، مع استثناءات للمستخدمين الموثوقين. إليك مثال على كيفية تنفيذ الـ Rate Limiting باستخدام مكتبة express-rate-limit في Node.js:

javascript
const rateLimit = require('express-rate-limit');
const RedisStore = require('rate-limit-redis');
const redis = require('redis');

const redisClient = redis.createClient({
 host: 'redis',
 port: 6379
});

const limiter = rateLimit({
 store: new RedisStore({
 sendCommand: (...args) => redisClient.sendCommand(args),
 }),
 windowMs: 15 * 60 * 1000, // 15 دقيقة
 max: 100, // حد أقصى 100 طلب
 message: 'Too many requests, please try again later.',
 keyGenerator: (req) => {
 return req.ip + ':' + req.headers['user-agent']; // تحديد المستخدم بناءً على IP و User-Agent
 },
 skip: (req) => {
 // استثناء للمستخدمين الموثوقين
 return req.user && req.user.role === 'admin';
 },
 onLimitReached: (req, res, options) => {
 console.log(`Rate limit reached for IP: ${req.ip}`);
 // يمكنك هنا إرسال تنبيه أو تسجيل الحدث
 }
});

app.use(limiter);

في هذا المثال، استخدمنا Redis لتخزين عدد الطلبات، مما يسمح بتوزيع الـ Rate Limiting على عدة سيرفرات. أيضاً، قمنا بتحديد المستخدم بناءً على IP و User-Agent، مما يمنع المهاجم من تغيير هويته بسهولة. لاحظ أننا استثنينا المستخدمين الإداريين من الحد، لكن يجب أن تكون حذراً مع هذا الاستثناء لأنه يمكن استغلاله إذا تم اختراق حساب إداري.

الـ Rate Limiting المتكيف: كيف تتعامل مع الهجمات الذكية

المهاجمون لا يستخدمون دائماً هجمات بسيطة. أحياناً، يستخدمون تقنيات متقدمة مثل الـ Slowloris أو الـ Low and Slow Attacks لإسقاط السيرفر دون الوصول إلى الحد الأقصى للطلبات. الحل هو استخدام Rate Limiting متكيف، حيث يتغير الحد بناءً على سلوك المستخدم. مثلاً، إذا لاحظت أن مستخدماً يرسل طلبات بوتيرة غير طبيعية، يمكنك خفض الحد له تلقائياً. إليك مثال على كيفية تنفيذ ذلك باستخدام مكتبة rate-limiter-flexible:

javascript
const { RateLimiterRedis } = require('rate-limiter-flexible');

const redisClient = require('redis').createClient({
 host: 'redis',
 port: 6379
});

const rateLimiter = new RateLimiterRedis({
 storeClient: redisClient,
 keyPrefix: 'rate_limit',
 points: 100, // 100 طلب
 duration: 60, // في 60 ثانية
 blockDuration: 60, // حظر لمدة 60 ثانية إذا تم تجاوز الحد
 inmemoryBlockOnConsumed: 101, // حظر مؤقت في الذاكرة
 inmemoryBlockDuration: 10 // لمدة 10 ثوانٍ
});

async function checkRateLimit(key) {
 try {
 const res = await rateLimiter.consume(key);
 return { allowed: true, remaining: res.remainingPoints };
 } catch (rejRes) {
 return { allowed: false, retryAfter: rejRes.msBeforeNext / 1000 };
 }
}

// استخدام متكيف
app.use(async (req, res, next) => {
 const key = req.ip + ':' + req.path;
 const rateLimitResult = await checkRateLimit(key);
 
 if (!rateLimitResult.allowed) {
 return res.status(429).json({
 error: 'Too many requests',
 retryAfter: rateLimitResult.retryAfter
 });
 }
 
 // إذا كان المستخدم قريباً من الحد، خفض الحد له تلقائياً
 if (rateLimitResult.remaining < 10) {
 await rateLimiter.set(key, 50, 60); // خفض الحد إلى 50 طلب
 }
 
 next();
});

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


5. التشفير: لا تكتفِ بـ HTTPS

الكثير من المطورين يعتقدون أن استخدام HTTPS يكفي لتأمين البيانات. لكن الحقيقة أن HTTPS يحمي البيانات أثناء النقل فقط، وليس أثناء التخزين أو المعالجة. مثلاً، في عام 2014، تعرضت شركة Sony Pictures لهجوم أدى إلى تسريب بيانات حساسة. السبب؟ تخزين البيانات بدون تشفير، مما سمح للمهاجمين بقراءة البيانات بسهولة بعد اختراق الشبكة الداخلية.

الحل هو استخدام التشفير الشامل: تشفير البيانات أثناء النقل (HTTPS)، أثناء التخزين (Encryption at Rest)، وأثناء المعالجة (Homomorphic Encryption إذا أمكن). أيضاً، يجب استخدام خوارزميات تشفير قوية مثل AES-256 للتخزين و RSA-4096 للمفاتيح. إليك مثال على كيفية تشفير البيانات قبل تخزينها في قاعدة البيانات باستخدام Node.js:

javascript
const crypto = require('crypto');

const algorithm = 'aes-256-cbc';
const key = crypto.randomBytes(32); // مفتاح سري بطول 256 بت
const iv = crypto.randomBytes(16); // متجه تهيئة بطول 128 بت

function encrypt(text) {
 const cipher = crypto.createCipheriv(algorithm, key, iv);
 let encrypted = cipher.update(text, 'utf8', 'hex');
 encrypted += cipher.final('hex');
 return {
 iv: iv.toString('hex'),
 encryptedData: encrypted
 };
}

function decrypt(encrypted) {
 const iv = Buffer.from(encrypted.iv, 'hex');
 const encryptedText = encrypted.encryptedData;
 const decipher = crypto.createDecipheriv(algorithm, key, iv);
 let decrypted = decipher.update(encryptedText, 'hex', 'utf8');
 decrypted += decipher.final('utf8');
 return decrypted;
}

// مثال على الاستخدام
const sensitiveData = 'This is a secret!';
const encrypted = encrypt(sensitiveData);
console.log('Encrypted:', encrypted);

const decrypted = decrypt(encrypted);
console.log('Decrypted:', decrypted);

لاحظ أننا استخدمنا AES-256 مع متجه تهيئة عشوائي لكل عملية تشفير. هذا يمنع المهاجم من استخدام هجمات مثل الـ Known-Plaintext Attack. أيضاً، يجب تخزين المفتاح السري في مكان آمن مثل AWS KMS أو HashiCorp Vault، وليس في الكود أو قاعدة البيانات. لكن حتى مع كل هذه الإجراءات، لا يزال هناك خطر: ماذا لو تمكن المهاجم من الوصول إلى المفاتيح نفسها؟ هنا يأتي دور إدارة المفاتيح.

إدارة المفاتيح: لا تخزن المفاتيح في الكود أبداً

أحد أكبر الأخطاء التي يقع فيها المطورون هو تخزين المفاتيح السرية في الكود أو في ملفات التكوين. مثلاً، في عام 2019، تم اكتشاف أن شركة Uber تركت مفتاح AWS الخاص بها في مستودع عام على GitHub، مما سمح للمهاجمين بالوصول إلى بيانات حساسة. الحل هو استخدام خدمات إدارة المفاتيح مثل AWS KMS أو HashiCorp Vault، حيث يتم تخزين المفاتيح بشكل آمن ويمكن الوصول إليها فقط من خلال طلبات مصادقة.

إليك مثال على كيفية استخدام AWS KMS لتشفير وفك تشفير البيانات في Node.js:

javascript
const AWS = require('aws-sdk');
const kms = new AWS.KMS();

async function encryptWithKMS(data, keyId) {
 const params = {
 KeyId: keyId,
 Plaintext: Buffer.from(data)
 };
 
 try {
 const result = await kms.encrypt(params).promise();
 return result.CiphertextBlob.toString('base64');
 } catch (err) {
 throw new Error(`KMS Encryption Error: ${err.message}`);
 }
}

async function decryptWithKMS(encryptedData) {
 const params = {
 CiphertextBlob: Buffer.from(encryptedData, 'base64')
 };
 
 try {
 const result = await kms.decrypt(params).promise();
 return result.Plaintext.toString('utf8');
 } catch (err) {
 throw new Error(`KMS Decryption Error: ${err.message}`);
 }
}

// مثال على الاستخدام
(async () => {
 const keyId = 'arn:aws:kms:us-east-1:123456789012:key/abcd1234-5678-90ef-ghij-klmnopqrstuv';
 const data = 'This is a secret!';
 
 const encrypted = await encryptWithKMS(data, keyId);
 console.log('Encrypted:', encrypted);
 
 const decrypted = await decryptWithKMS(encrypted);
 console.log('Decrypted:', decrypted);
})();

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


6. المراقبة والتدقيق: كيف تكتشف الهجمات قبل أن تسبب ضرراً

المراقبة والتدقيق هما آخر خط دفاع ضد الهجمات. بدونهما، قد لا تعرف أبداً أن نظامك تعرض لهجوم حتى فوات الأوان. مثلاً، في عام 2013، تعرضت شركة Target لهجوم أدى إلى تسريب بيانات 40 مليون بطاقة ائتمان. السبب؟ عدم وجود نظام مراقبة فعال، مما سمح للمهاجمين بالبقاء في النظام لمدة أسابيع دون اكتشافهم.

الحل هو استخدام أدوات مراقبة متقدمة مثل ELK Stack (Elasticsearch, Logstash, Kibana) أو Datadog، مع تفعيل التنبيهات الآلية. أيضاً، يجب تسجيل كل طلب إلى API مع تفاصيل كافية مثل: عنوان IP، نوع الطلب، الوقت، المستخدم، والنتائج. إليك مثال على كيفية تسجيل الطلبات باستخدام Winston في Node.js:

javascript
const winston = require('winston');
const { combine, timestamp, json } = winston.format;

const logger = winston.createLogger({
 level: 'info',
 format: combine(timestamp(), json()),
 transports: [
 new winston.transports.File({ filename: 'api.log' }),
 new winston.transports.Console()
 ]
});

app.use((req, res, next) => {
 const start = Date.now();
 
 res.on('finish', () => {
 const duration = Date.now() - start;
 logger.info({
 ip: req.ip,
 method: req.method,
 path: req.path,
 status: res.statusCode,
 duration: `${duration}ms`,
 user: req.user ? req.user.id : 'anonymous',
 userAgent: req.headers['user-agent']
 });
 });
 
 next();
});

// مثال على تنبيه عند اكتشاف نشاط مشبوه
app.use((req, res, next) => {
 if (req.path === '/admin' && req.ip !== 'trusted-ip') {
 logger.warn({
 message: 'Unauthorized access attempt to admin panel',
 ip: req.ip,
 user: req.user ? req.user.id : 'anonymous'
 });
 // يمكنك هنا إرسال تنبيه عبر البريد الإلكتروني أو Slack
 }
 next();
});

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

السجلات غير القابلة للتعديل: كيف تضمن أن السجلات لا تُمسح أو تُعدل

المهاجمون غالباً ما يحاولون مسح السجلات لتغطية آثارهم. الحل هو استخدام أنظمة تسجيل غير قابلة للتعديل مثل AWS CloudTrail أو Google Cloud Audit Logs، حيث يتم تخزين السجلات في مكان آمن ولا يمكن تعديلها. إليك مثال على كيفية إرسال السجلات إلى AWS CloudWatch:

javascript
const AWS = require('aws-sdk');
const cloudwatch = new AWS.CloudWatchLogs();

async function logToCloudWatch(logGroup, logStream, message) {
 const params = {
 logGroupName: logGroup,
 logStreamName: logStream,
 logEvents: [
 {
 timestamp: Date.now(),
 message: JSON.stringify(message)
 }
 ]
 };
 
 try {
 await cloudwatch.putLogEvents(params).promise();
 } catch (err) {
 console.error('CloudWatch Log Error:', err);
 }
}

// استخدام في middleware
app.use((req, res, next) => {
 res.on('finish', () => {
 const logData = {
 ip: req.ip,
 method: req.method,
 path: req.path,
 status: res.statusCode,
 user: req.user ? req.user.id : 'anonymous'
 };
 logToCloudWatch('my-api-logs', 'requests', logData);
 });
 next();
});

في هذا المثال، استخدمنا AWS CloudWatch لتخزين السجلات في مكان آمن وغير قابل للتعديل. أيضاً، يمكنك تفعيل التنبيهات الآلية عند اكتشاف نشاط مشبوه، مثل عدد كبير من الطلبات الفاشلة من نفس IP. لكن حتى مع كل هذه الإجراءات، لا يزال هناك شيء واحد يجب أن تتذكره دائماً: الأمان ليس منتجاً، بل عملية مستمرة.


خلاصة المهندس: لا تتوقف عند الـ Checklist

تأمين API ليس مجرد إضافة JWT أو Rate Limiting ثم نسيان الأمر. الأمان عملية مستمرة تتطلب مراقبة وتحديثاً دائماً. من تجربتي، أكبر خطأ يقع فيه المطورون هو الاعتماد على الـ Checklist دون فهم السبب وراء كل خطوة. مثلاً، لماذا نستخدم JWKS بدلاً من المفتاح السري مباشرة؟ لماذا نحتاج إلى Rate Limiting متكيف؟ لماذا يجب تشفير البيانات حتى في قاعدة البيانات؟ الإجابة ليست فقط

لأنها أفضل ممارسة

، بل لأن كل خطوة تحل مشكلة حقيقية واجهتها شركات كبيرة ودفعت ثمنها غالياً. لذا، نصيحتي لك: لا تتوقف عند الـ Checklist. افهم المشكلة وراء كل حل، واختبر نظامك بانتظام باستخدام أدوات مثل OWASP ZAP أو Burp Suite. وتذكر دائماً: المهاجم يحتاج إلى ثغرة واحدة فقط، بينما عليك تأمين كل شيء.

الأمان ليس منتجاً، بل عملية. إذا كنت تعتقد أن الأمن مكلف، جرب الإهمال.

— بروس شناير، خبير أمن المعلومات
API Security Authentication Rate Limiting JWT Cybersecurity

التعليقات

العودة للمقالات
نوفيل

منصة تعليم البرمجة الأولى بالعربي. تعلم من الصفر حتى الاحتراف مع كورسات احترافية وتحديات ذكاء اصطناعي.

المنصة

  • الكورسات
  • التحديات
  • المقالات
  • الأدوات

الحساب

  • إنشاء حساب
  • تسجيل الدخول
  • لوحة التحكم
  • الملف الشخصي

روابط

  • سياسة الخصوصية
  • شروط الاستخدام
  • عن نوفيل
  • تواصل معنا

© 2026 نوفيل. جميع الحقوق محفوظة.

صُنع بـ في مصر