API غير آمن هو بوابة مفتوحة للهاكرز. في هذا الدليل العملي، سنفكك كل طبقة من طبقات الحماية: من JWT وOAuth2 إلى Rate Limiting وCORS، مع أكواد حقيقية وحالات واقعية من شركات مثل تويتر وجيت هاب.
في عام ٢٠٢٢، تعرضت شركة أوبر لاختراق ضخم بسبب ثغرة في API لم يتم تأمينها بشكل صحيح. المهاجم استطاع تجاوز طبقة Authentication ببساطة لأن السيرفر كان يقبل أي طلب يحمل توكين غير موقّع. النتيجة؟ بيانات ٥٧ مليون مستخدم مسربة. هذا ليس سيناريو خيالياً، بل واقع يحدث يومياً للشركات الصغيرة والكبيرة على حد سواء. المشكلة ليست في وجود ثغرات فقط، بل في أن معظم المطورين لا يعرفون كيف تعمل طبقات الحماية خلف الكواليس، مما يجعلهم يكتبون كوداً أمنياً سطحياً لا يصمد أمام الهجمات الحقيقية.
API هو العمود الفقري لأي تطبيق حديث، لكن معظم المطورين يعاملونه كطريق سريع مفتوح دون بوابات تفتيش. الحقيقة هي أن تأمين API ليس مجرد إضافة مكتبة هنا وهناك، بل هو سلسلة من القرارات التقنية التي يجب اتخاذها في كل طبقة من طبقات النظام. من المصادقة (Authentication) إلى التحكم في معدل الطلبات (Rate Limiting)، كل خطوة لها تأثير مباشر على أداء النظام وأمانه. في هذا المقال، سنذهب عميقاً في التفاصيل التقنية، لنرى كيف تعمل هذه الطبقات معاً، وأين تكمن الفخاخ التي يقع فيها حتى المطورون المحترفون.
عندما نتحدث عن Authentication في APIs، أول ما يخطر في البال هو JWT (JSON Web Tokens). الجميع يستخدمه، لكن قليلون يفهمون كيف يعمل حقاً. JWT ليس مجرد سلسلة عشوائية من الأحرف، بل هو ثلاث أجزاء منفصلة: الـ Header الذي يحتوي على نوع التوكين والخوارزمية المستخدمة للتوقيع، الـ Payload الذي يحمل البيانات مثل معرف المستخدم والصلاحيات، وأخيراً الـ Signature الذي يضمن سلامة التوكين. المشكلة هنا أن الكثير من المطورين يعتقدون أن مجرد استخدام JWT يعني أن النظام آمن، وهذا أبعد ما يكون عن الحقيقة.
لنأخذ مثالاً عملياً: إذا استخدمت خوارزمية HS256 لتوقيع التوكين، فإن السيرفر يستخدم مفتاحاً سرياً (secret key) للتحقق من صحة التوقيع. لكن ماذا لو تم تسريب هذا المفتاح؟ فجأة، أي شخص يستطيع توليد توكينات مزيفة والتسلل إلى النظام. هذا بالضبط ما حدث لشركة تويتر في عام ٢٠٢٠، عندما تم تسريب مفتاح سري من قاعدة بيانات غير مؤمنة. الحل؟ استخدام خوارزميات غير متماثلة مثل RS256، حيث يكون هناك مفتاح خاص للتوقيع ومفتاح عام للتحقق. بهذه الطريقة، حتى لو تم تسريب المفتاح العام، لا يمكن لأحد توليد توكينات جديدة.
// مثال على توليد JWT باستخدام RS256 في Node.js
const jwt = require('jsonwebtoken');
const fs = require('fs');
// قراءة المفتاح الخاص للتوقيع
const privateKey = fs.readFileSync('private.key', 'utf8');
// توليد التوكين
const token = jwt.sign(
{ userId: 123, role: 'admin' },
privateKey,
{ algorithm: 'RS256', expiresIn: '1h' }
);
console.log(token);
// للتحقق من التوكين
const publicKey = fs.readFileSync('public.key', 'utf8');
jwt.verify(token, publicKey, { algorithms: ['RS256'] }, (err, decoded) => {
if (err) {
console.error('Invalid token:', err.message);
} else {
console.log('Decoded token:', decoded);
}
});لكن حتى مع استخدام RS256، هناك مشكلة أخرى شائعة: عدم التحقق من الـ Payload بشكل صحيح. مثلاً، إذا كان التوكين يحتوي على حقل role، يجب على السيرفر التحقق من هذا الحقل في كل طلب، وليس فقط الاعتماد على التوقيع. في عام ٢٠١٨، اكتشف باحث أمني ثغرة في API لشركة جيت هاب تسمح لأي مستخدم بتغيير دوره إلى admin ببساطة عن طريق تعديل حقل role في التوكين وإعادة توقيعه باستخدام مفتاح عام معروف. الحل؟ تخزين البيانات الحساسة في قاعدة البيانات والتحقق منها في كل طلب، وعدم الاعتماد فقط على ما يأتي في التوكين.
OAuth2 هو بروتوكول معياري للمصادقة التفويضية، لكنه غالباً ما يُساء استخدامه. الفكرة الأساسية وراء OAuth2 هي السماح للتطبيقات بالوصول إلى موارد المستخدم دون الحاجة إلى مشاركة كلمة المرور. لكن المشكلة تكمن في أن الكثير من المطورين يستخدمون OAuth2 بطريقة خاطئة، مما يؤدي إلى ثغرات أمنية خطيرة. مثلاً، استخدام grant type غير مناسب للنوع التطبيق يمكن أن يؤدي إلى تسريب التوكينات.
لنأخذ grant type Authorization Code مع PKCE، الذي يعتبر الأكثر أماناً للتطبيقات الموبايل والويب. هنا، التطبيق يرسل المستخدم إلى صفحة تسجيل الدخول الخاصة بمزود الهوية (مثل جوجل أو فيسبوك)، وبعد المصادقة، يحصل على رمز تفويض (authorization code) يتم استبداله بتوكين وصول (access token). لكن إذا استخدمنا grant type Implicit بدلاً من ذلك، فإن التوكين يتم إرساله مباشرة في عنوان URL، مما يجعله عرضة للهجمات مثل سرقة التوكين عبر سجلات المتصفح أو الهجمات عبر وسطاء (man-in-the-middle).
# مثال على استخدام OAuth2 مع Flask وAuthorization Code Flow
from flask import Flask, request, redirect, session
import requests
import secrets
app = Flask(__name__)
app.secret_key = secrets.token_hex(32)
# إعدادات OAuth2
CLIENT_ID = 'your_client_id'
CLIENT_SECRET = 'your_client_secret'
AUTHORIZATI 'https://provider.com/oauth/authorize'
TOKEN_URL = 'https://provider.com/oauth/token'
REDIRECT_URI = 'https://yourapp.com/callback'
@app.route('/login')
def login():
# إنشاء رمز تحدي PKCE
code_verifier = secrets.token_urlsafe(64)
session['code_verifier'] = code_verifier
code_challenge = code_verifier # في الواقع، يجب تجزئة هذا الرمز باستخدام SHA-256
# بناء عنوان URL للتوجيه
auth_url = (
f"{AUTHORIZATION_BASE_URL}?response_type=code&client_id={CLIENT_ID}"
f"&redirect_uri={REDIRECT_URI}&code_challenge={code_challenge}"
f"&code_challenge_method=S256"
)
return redirect(auth_url)
@app.route('/callback')
def callback():
code = request.args.get('code')
code_verifier = session.pop('code_verifier', None)
if not code or not code_verifier:
return 'Authorization failed', 400
# استبدال رمز التفويض بتوكين الوصول
token_response = requests.post(
TOKEN_URL,
data={
'grant_type': 'authorization_code',
'code': code,
'redirect_uri': REDIRECT_URI,
'client_id': CLIENT_ID,
'client_secret': CLIENT_SECRET,
'code_verifier': code_verifier
}
)
if token_response.status_code != 200:
return 'Failed to obtain access token', 400
access_token = token_response.json().get('access_token')
session['access_token'] = access_token
return redirect('/protected-resource')المصادقة (Authentication) تخبرك من هو المستخدم، لكن التفويض (Authorization) يحدد ما يمكنه فعله. هذه هي الخطوة التي يغفل عنها الكثير من المطورين، مما يؤدي إلى ثغرات مثل الوصول غير المصرح به إلى البيانات. مثلاً، إذا كان لديك API يسمح للمستخدمين بعرض بياناتهم الخاصة، يجب عليك التحقق في كل طلب أن المستخدم الذي يرسل الطلب هو بالفعل مالك البيانات. هذا يبدو بديهياً، لكن في الواقع، الكثير من APIs تعتمد على افتراضات خاطئة.
لنأخذ مثالاً من تجربة شخصية: في أحد المشاريع التي عملت عليها، كان لدينا API يسمح للمستخدمين بعرض ملفاتهم المخزنة على السحابة. الكود كان يتحقق من وجود التوكين في الطلب، لكنه لم يتحقق من أن الملف المطلوب ينتمي بالفعل للمستخدم الذي أرسل الطلب. النتيجة؟ أي مستخدم كان يستطيع الوصول إلى ملفات المستخدمين الآخرين ببساطة عن طريق تغيير معرف الملف في عنوان URL. الحل؟ إضافة طبقة تحقق إضافية في قاعدة البيانات تتحقق من ملكية الملف قبل إرساله.
// مثال على التحقق من ملكية الملف في Node.js مع Express
const express = require('express');
const { Pool } = require('pg');
const jwt = require('jsonwebtoken');
const app = express();
const pool = new Pool();
app.get('/files/:fileId', async (req, res) => {
try {
// التحقق من التوكين
const token = req.headers.authorization?.split(' ')[1];
if (!token) return res.status(401).send('Unauthorized');
const decoded = jwt.verify(token, process.env.PUBLIC_KEY, { algorithms: ['RS256'] });
const userId = decoded.userId;
// التحقق من ملكية الملف
const { fileId } = req.params;
const query = 'SELECT * FROM files WHERE id = $1 AND user_id = $2';
const { rows } = await pool.query(query, [fileId, userId]);
if (rows.length === 0) {
return res.status(403).send('Forbidden: File does not belong to you');
}
res.json(rows[0]);
} catch (err) {
console.error(err);
res.status(500).send('Internal Server Error');
}
});
app.listen(3000, () => console.log('Server running on port 3000'));هناك أيضاً مفهوم الـ Role-Based Access Control (RBAC) وAttribute-Based Access Control (ABAC)، حيث يتم التحكم في الوصول بناءً على الأدوار أو السمات. مثلاً، في نظام إدارة المحتوى، قد يكون لديك أدوار مثل admin، editor، وviewer، كل منها له صلاحيات مختلفة. لكن حتى مع RBAC، يجب أن تكون حذراً من الثغرات مثل تخزين الصلاحيات في التوكين دون التحقق منها في قاعدة البيانات. في عام ٢٠١٩، اكتشف باحث أمني ثغرة في API لشركة مايكروسوفت تسمح لأي مستخدم بتغيير دوره إلى admin ببساطة عن طريق تعديل حقل role في التوكين، لأن السيرفر كان يعتمد فقط على ما يأتي في التوكين دون التحقق من قاعدة البيانات.
Rate Limiting هو أحد أكثر الأدوات فعالية لمنع الهجمات مثل DDoS وسرقة البيانات، لكنه غالباً ما يُنفذ بشكل سطحي. الفكرة الأساسية هي تحديد عدد الطلبات التي يمكن للمستخدم إرسالها في فترة زمنية معينة، لكن التنفيذ الصحيح يتطلب فهماً عميقاً لكيفية عمل النظام خلف الكواليس. مثلاً، إذا استخدمت مكتبة مثل express-rate-limit في Node.js دون فهم كيفية تخزين العدادات، قد ينتهي بك الأمر إلى استنزاف ذاكرة السيرفر أو جعل النظام بطيئاً للغاية.
المشكلة الأكبر في Rate Limiting هي تحديد ما يعتبر "مستخدماً". هل هو عنوان IP؟ هل هو التوكين؟ إذا استخدمت عنوان IP فقط، فقد تواجه مشكلة مع المستخدمين الذين يتشاركون نفس الشبكة (مثل في مقهى أو شركة)، حيث يمكن أن يتم حظرهم جميعاً إذا تجاوز أحدهم الحد. وإذا استخدمت التوكين فقط، فقد يتمكن المهاجم من تجاوز الحد ببساطة عن طريق توليد توكينات جديدة باستمرار. الحل؟ استخدام مزيج من الاثنين، بالإضافة إلى عوامل أخرى مثل بصمة المتصفح أو سلوك المستخدم.
// مثال على Rate Limiting باستخدام Redis في Node.js
const express = require('express');
const redis = require('redis');
const { RateLimiterRedis } = require('rate-limiter-flexible');
const app = express();
const redisClient = redis.createClient({
host: 'localhost',
port: 6379
});
// إعداد Rate Limiter
const rateLimiter = new RateLimiterRedis({
storeClient: redisClient,
keyPrefix: 'rate_limit',
points: 100, // عدد الطلبات المسموح بها
duration: 60, // في ثواني
blockDuration: 60 // مدة الحظر في ثواني
});
// تطبيق Rate Limiting على جميع الطلبات
app.use(async (req, res, next) => {
try {
// استخدام عنوان IP والتوكين كمفتاح
const key = req.ip + (req.headers.authorization || '');
await rateLimiter.consume(key);
next();
} catch (err) {
res.status(429).send('Too Many Requests');
}
});
app.get('/api/data', (req, res) => {
res.json({ message: 'Data retrieved successfully' });
});
app.listen(3000, () => console.log('Server running on port 3000'));هناك أيضاً استراتيجيات متقدمة مثل Token Bucket وLeaky Bucket، التي تسمح بتحديد معدل ثابت للطلبات بدلاً من السماح بجميع الطلبات في بداية الفترة الزمنية. مثلاً، بدلاً من السماح بـ ١٠٠ طلب في الدقيقة، يمكنك السماح بطلب واحد كل ٦٠٠ مللي ثانية، مما يجعل النظام أكثر استقراراً. في عام ٢٠٢١، تعرضت شركة Cloudflare لهجوم DDoS ضخم، لكنهم استطاعوا التصدي له بفضل استخدامهم لاستراتيجية Leaky Bucket التي تسمح بتوزيع الحمل بشكل متساوٍ على مدار الوقت بدلاً من السماح بجميع الطلبات في لحظة واحدة.
Rate Limiting ليس حلاً سحرياً، وفي بعض الحالات يمكن أن يكون ضاراً. مثلاً، إذا قمت بتطبيق Rate Limiting على واجهة برمجة التطبيقات العامة (Public API) الخاصة بك دون مراعاة أن بعض المستخدمين قد يكون لديهم احتياجات مشروعة لتجاوز الحد، فقد تفقد عملاء مهمين. في عام ٢٠٢٠، قررت شركة تويتر تطبيق Rate Limiting صارم على API الخاص بها، مما أدى إلى توقف العديد من التطبيقات التابعة لجهات خارجية عن العمل، مما أثار غضب المطورين.
الحل؟ استخدام استراتيجيات مرنة مثل السماح للمستخدمين بطلب زيادة الحد مؤقتاً، أو استخدام مفاتيح API مختلفة لكل عميل مع حدود مختلفة. مثلاً، يمكنك السماح للعملاء المجانيين بـ ١٠٠ طلب في الدقيقة، بينما تسمح للعملاء المدفوعين بـ ١٠٠٠ طلب في الدقيقة. بالإضافة إلى ذلك، يجب أن تكون رسائل الخطأ واضحة ومفيدة، بدلاً من مجرد إرسال 429 Too Many Requests دون شرح. على سبيل المثال، يمكنك إرسال رسالة مثل: "لقد تجاوزت الحد المسموح به وهو ١٠٠ طلب في الدقيقة. يمكنك طلب زيادة الحد من خلال لوحة التحكم الخاصة بك."
CORS (Cross-Origin Resource Sharing) وCSRF (Cross-Site Request Forgery) هما من أكثر الثغرات شيوعاً في APIs، لكن الكثير من المطورين لا يفهمون كيف يعملان حقاً. CORS هو آلية تسمح للمتصفح بالتحكم في كيفية تفاعل الصفحات من نطاقات مختلفة مع بعضها البعض، بينما CSRF هو هجوم يسمح للمهاجم بتنفيذ إجراءات نيابة عن المستخدم دون علمه. المشكلة هنا أن الكثير من المطورين يعاملون CORS كحل أمني، بينما هو في الواقع مجرد آلية للتحكم في الوصول من جانب المتصفح.
لنبدأ بـ CORS: إذا قمت بتعيين headers بشكل خاطئ، فقد تسمح لأي موقع بالوصول إلى API الخاص بك. مثلاً، إذا استخدمت Access-Control-Allow-Origin: *، فإنك تسمح لأي موقع على الإنترنت بإرسال طلبات إلى API الخاص بك وقراءة الاستجابات. هذا قد يبدو غير ضار، لكنه يمكن أن يؤدي إلى تسريب البيانات إذا كان API الخاص بك يحتوي على معلومات حساسة. في عام ٢٠١٨، اكتشف باحث أمني ثغرة في API لشركة جوجل تسمح لأي موقع بقراءة بيانات المستخدمين ببساطة لأن Access-Control-Allow-Origin كان مضبوطاً على *.
// مثال على إعداد CORS بشكل آمن في Express
const express = require('express');
const cors = require('cors');
const app = express();
// قائمة النطاقات المسموح بها
const allowedOrigins = [
'https://yourapp.com',
'https://sub.yourapp.com',
'https://yourapp.dev'
];
// إعداد CORS
app.use(cors({
origin: function (origin, callback) {
// السماح للطلبات من تطبيقات الموبايل أو أدوات التطوير
if (!origin) return callback(null, true);
if (allowedOrigins.includes(origin)) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
},
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true
}));
app.get('/api/data', (req, res) => {
res.json({ message: 'This is secure data' });
});
app.listen(3000, () => console.log('Server running on port 3000'));أما CSRF، فهو هجوم أكثر تعقيداً. الفكرة هنا أن المهاجم يمكن أن يخدع المستخدم لإرسال طلب إلى API الخاص بك دون علمه. مثلاً، إذا كان المستخدم مسجلاً دخول في موقعك، يمكن للمهاجم إرسال رابط يحتوي على طلب POST إلى API الخاص بك، وعندما ينقر المستخدم على الرابط، يتم إرسال الطلب نيابة عنه. الحل؟ استخدام CSRF tokens، وهي رموز عشوائية يتم توليدها لكل جلسة وتضمينها في كل طلب. لكن حتى هنا، يجب أن تكون حذراً من كيفية تخزين هذه الرموز وإرسالها.
# مثال على حماية CSRF في Flask
from flask import Flask, request, session, render_template_string
import secrets
app = Flask(__name__)
app.secret_key = secrets.token_hex(32)
@app.route('/form', methods=['GET', 'POST'])
def form():
if request.method == 'GET':
# توليد CSRF token وتخزينه في الجلسة
session['csrf_token'] = secrets.token_hex(16)
return render_template_string('''
<form method="POST" action="/form">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
<input type="text" name="data">
<button type="submit">Submit</button>
</form>
''', csrf_token=session['csrf_token'])
elif request.method == 'POST':
# التحقق من CSRF token
if request.form.get('csrf_token') != session.get('csrf_token'):
return 'CSRF token mismatch', 403
data = request.form.get('data')
return f'Data received: {data}'
if __name__ == '__main__':
app.run(debug=True)هناك أيضاً استراتيجيات متقدمة مثل SameSite Cookies، التي تمنع المتصفح من إرسال الكوكيز مع الطلبات التي تأتي من نطاقات مختلفة. مثلاً، إذا قمت بتعيين SameSite=Lax أو SameSite=Strict، فإن المتصفح لن يرسل الكوكيز مع الطلبات التي تأتي من مواقع أخرى، مما يمنع هجمات CSRF بشكل فعال. في عام ٢٠٢١، قررت شركة جوجل جعل SameSite=Lax الإعداد الافتراضي للكوكيز في متصفح كروم، مما قلل بشكل كبير من عدد هجمات CSRF على مستوى الإنترنت.
HTTPS هو المعيار الذهبي للنقل الآمن للبيانات، لكنه ليس كافياً لحماية API الخاص بك. المشكلة هنا أن HTTPS يحمي البيانات أثناء النقل فقط، لكنه لا يحميها عندما تكون مخزنة على السيرفر أو في قاعدة البيانات. بالإضافة إلى ذلك، هناك هجمات مثل MITM (Man-in-the-Middle) التي يمكن أن تستغل ثغرات في تنفيذ HTTPS نفسه. مثلاً، إذا استخدمت مكتبة قديمة لتشفير البيانات، قد تكون عرضة لهجمات مثل POODLE أو Heartbleed.
الحل؟ استخدام تشفير شامل (End-to-End Encryption)، حيث يتم تشفير البيانات على جهاز العميل قبل إرسالها إلى السيرفر، وتبقى مشفرة حتى تصل إلى المستلم المقصود. هذا يعني أن حتى إذا تم اختراق السيرفر، فإن البيانات تظل غير قابلة للقراءة. في عام ٢٠٢٠، قررت شركة واتساب تطبيق تشفير شامل لجميع الرسائل، مما جعلها واحدة من أكثر تطبيقات المراسلة أماناً في العالم. لكن حتى مع التشفير الشامل، يجب أن تكون حذراً من كيفية إدارة المفاتيح. إذا تم تخزين المفاتيح على السيرفر، فإن المهاجم الذي يخترق السيرفر يمكن أن يحصل على المفاتيح ويفك تشفير البيانات.
// مثال على تشفير البيانات على جانب العميل باستخدام Web Crypto API
async function encryptData(data, publicKey) {
// تحويل البيانات إلى ArrayBuffer
const dataBuffer = new TextEncoder().encode(data);
// تشفير البيانات باستخدام RSA-OAEP
const encryptedData = await window.crypto.subtle.encrypt(
{
name: "RSA-OAEP"
},
publicKey,
dataBuffer
);
return encryptedData;
}
async function decryptData(encryptedData, privateKey) {
// فك تشفير البيانات باستخدام RSA-OAEP
const decryptedData = await window.crypto.subtle.decrypt(
{
name: "RSA-OAEP"
},
privateKey,
encryptedData
);
return new TextDecoder().decode(decryptedData);
}
// مثال على الاستخدام
(async () => {
// توليد زوج مفاتيح
const keyPair = await window.crypto.subtle.generateKey(
{
name: "RSA-OAEP",
modulusLength: 2048,
publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
hash: "SHA-256"
},
true,
["encrypt", "decrypt"]
);
const data = "Sensitive data";
const encrypted = await encryptData(data, keyPair.publicKey);
const decrypted = await decryptData(encrypted, keyPair.privateKey);
console.log('Original:', data);
console.log('Decrypted:', decrypted);
})();هناك أيضاً مفهوم الـ Perfect Forward Secrecy (PFS)، الذي يضمن أنه حتى إذا تم تسريب مفتاح خاص في المستقبل، لا يمكن فك تشفير البيانات التي تم إرسالها في الماضي. هذا يتم تحقيقه باستخدام خوارزميات مثل Diffie-Hellman Ephemeral (DHE) أو Elliptic Curve Diffie-Hellman Ephemeral (ECDHE)، حيث يتم توليد مفاتيح جلسة جديدة لكل اتصال. في عام ٢٠١٣، قررت شركة جوجل تطبيق PFS على جميع خدماتها، مما جعلها أكثر مقاومة للهجمات المستقبلية.
حتى إذا كانت البيانات آمنة أثناء النقل، فإنها قد تكون عرضة للخطر عندما تكون مخزنة على السيرفر. مثلاً، إذا تم تخزين كلمات المرور أو التوكينات في قاعدة البيانات دون تشفير، فإن اختراق قاعدة البيانات يعني أن جميع البيانات أصبحت في أيدي المهاجمين. الحل؟ استخدام تقنيات مثل التجزئة (Hashing) والتشفير المتماثل (Symmetric Encryption) لحماية البيانات الحساسة.
لنأخذ كلمات المرور كمثال: يجب ألا تخزن كلمات المرور أبداً بشكلها الخام، بل يجب تجزئتها باستخدام خوارزميات قوية مثل Argon2 أو bcrypt. هذه الخوارزميات مصممة لتكون بطيئة عن قصد، مما يجعل من الصعب على المهاجمين استخدام هجمات القوة الغاشمة (Brute Force). في عام ٢٠١٢، تم اختراق قاعدة بيانات LinkedIn، وكشفت الشركة أن كلمات المرور كانت مجزأة باستخدام SHA-1 دون إضافة ملح (salt)، مما جعل من السهل على المهاجمين فك تشفير ملايين كلمات المرور.
# مثال على تجزئة كلمات المرور باستخدام Argon2 في Python
import argon2
# إعداد Argon2
ph = argon2.PasswordHasher(
time_cost=3, # عدد التكرارات
memory_cost=65536, # استخدام 64MB من الذاكرة
parallelism=4, # عدد الخيوط
hash_len=32, # طول التجزئة
salt_len=16 # طول الملح
)
# تجزئة كلمة المرور
password = "my_secure_password"
hashed_password = ph.hash(password)
print(f"Hashed password: {hashed_password}")
# التحقق من كلمة المرور
try:
ph.verify(hashed_password, password)
print("Password is correct")
except argon2.exceptions.VerifyMismatchError:
print("Password is incorrect")تأمين API ليس مهمة يمكن إنجازها مرة واحدة ونسيانها. إنها عملية مستمرة تتطلب مراقبة وتحديثات مستمرة. من تجربتي، أكبر خطأ يرتكبه المطورون هو الاعتماد على حل واحد فقط، مثل JWT أو Rate Limiting، معتقدين أن ذلك يكفي. الحقيقة هي أن الأمان هو سلسلة من الطبقات، وكل طبقة تضيف مستوى جديداً من الحماية. إذا فشل أحد الطبقات، يجب أن تكون الطبقات الأخرى جاهزة لسد الثغرة.
ابدأ بتطبيق المصادقة القوية باستخدام OAuth2 مع PKCE، وتأكد من أن كل طلب يتم التحقق منه بشكل صحيح. ثم أضف طبقة التفويض التي تتحقق من صلاحيات المستخدم في كل طلب. بعد ذلك، طبق Rate Limiting لمنع الهجمات وإدارة الحمل. لا تنسَ إعداد CORS وCSRF بشكل صحيح لمنع الهجمات من جانب المتصفح. وأخيراً، استخدم التشفير لحماية البيانات أثناء النقل وأثناء التخزين. ولا تنسَ مراقبة النظام باستمرار لاكتشاف أي نشاط مشبوه ومعالجته فوراً.
في النهاية، الأمان ليس مجرد كود تكتبه، بل هو عقلية يجب أن تتبناها في كل خطوة من خطوات تطوير API الخاص بك. كلما فكرت في الأمان من البداية، كلما قلّت المشاكل التي ستواجهها لاحقاً. تذكر: المهاجمون يبحثون دائماً عن أسهل طريق للدخول، فلا تجعل API الخاص بك هذا الطريق.