أمان الجلسات و JWT
أمان الجلسات و JWT
إدارة الجلسات والرموز هي مكونات حاسمة لأمان تطبيقات الويب. فهم كيفية تأمين الجلسات بشكل صحيح وتنفيذ JWT (رموز الويب JSON) يحمي من هجمات المصادقة الشائعة.
أساسيات أمان الجلسات
تحافظ الجلسات على حالة المستخدم عبر طلبات HTTP. بدون إجراءات أمان مناسبة، يمكن أن تكون الجلسات عرضة لهجمات مختلفة:
<?php
session_start();
// الإعدادات الافتراضية تكشف الثغرات
?>
<!-- تكوين جلسة آمن -->
<?php
ini_set('session.cookie_httponly', 1);
ini_set('session.cookie_secure', 1);
ini_set('session.cookie_samesite', 'Strict');
ini_set('session.use_only_cookies', 1);
ini_set('session.use_strict_mode', 1);
session_start();
?>
منع اختطاف الجلسات
يحدث اختطاف الجلسة عندما يسرق المهاجم معرف جلسة صالح لانتحال شخصية المستخدم. مطلوب طبقات دفاع متعددة:
// إعادة توليد معرف الجلسة عند تغيير الامتيازات
function secureLogin($userId) {
session_regenerate_id(true);
$_SESSION['user_id'] = $userId;
$_SESSION['ip_address'] = $_SERVER['REMOTE_ADDR'];
$_SESSION['user_agent'] = $_SERVER['HTTP_USER_AGENT'];
$_SESSION['last_activity'] = time();
}
// التحقق من الجلسة في كل طلب
function validateSession() {
if (!isset($_SESSION['user_id'])) {
return false;
}
// فحص عنوان IP (اختياري - قد يسبب مشاكل لمستخدمي الهاتف المحمول)
if ($_SESSION['ip_address'] !== $_SERVER['REMOTE_ADDR']) {
session_destroy();
return false;
}
// فحص وكيل المستخدم
if ($_SESSION['user_agent'] !== $_SERVER['HTTP_USER_AGENT']) {
session_destroy();
return false;
}
// فحص انتهاء مهلة الجلسة (30 دقيقة)
if (time() - $_SESSION['last_activity'] > 1800) {
session_destroy();
return false;
}
$_SESSION['last_activity'] = time();
return true;
}
?>
منع تثبيت الجلسة
هجمات تثبيت الجلسة تخدع المستخدمين لاستخدام معرف جلسة معروف. قم دائمًا بإعادة توليد معرفات الجلسات عند حدود المصادقة:
// قبل تسجيل الدخول - قد يعرف المهاجم هذا المعرف
session_start();
// بعد مصادقة ناجحة
if (authenticateUser($username, $password)) {
// إعادة توليد المعرف لإبطال الجلسة القديمة
session_regenerate_id(true);
$_SESSION['authenticated'] = true;
$_SESSION['user_id'] = $userId;
}
// أيضًا إعادة التوليد عند تسجيل الخروج
function logout() {
session_start();
$_SESSION = [];
session_destroy();
session_regenerate_id(true);
}
?>
سمات ملفات تعريف الارتباط الآمنة
تتحكم سمات ملفات تعريف الارتباط في كيفية تعامل المتصفحات مع ملفات تعريف ارتباط الجلسة. توفر كل سمة حماية أمنية محددة:
// تعيين سمات ملفات تعريف ارتباط آمنة
session_set_cookie_params([
'lifetime' => 0, // ملف تعريف ارتباط الجلسة (ينتهي عند إغلاق المتصفح)
'path' => '/', // متاح على مستوى الموقع
'domain' => '.example.com', // تضمين النطاقات الفرعية
'secure' => true, // HTTPS فقط
'httponly' => true, // لا يوجد وصول JavaScript
'samesite' => 'Strict' // حماية CSRF
]);
session_start();
?>
Secure بروتوكول HTTPS. تمنع سمة HttpOnly هجمات XSS من سرقة ملفات تعريف الارتباط. تمنع سمة SameSite هجمات CSRF.أساسيات JWT (رموز الويب JSON)
رموز JWT هي رموز عديمة الحالة تحتوي على مطالبات مشفرة. تتكون من ثلاثة أجزاء: الرأس والحمولة والتوقيع:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9. // الرأس (base64)
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4ifQ. // الحمولة (base64)
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c // التوقيع
<?php
// إنشاء JWT باستخدام PHP
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
$secretKey = getenv('JWT_SECRET_KEY');
$issuedAt = time();
$expirationTime = $issuedAt + 3600; // ساعة واحدة
$payload = [
'iss' => 'example.com', // المُصدر
'aud' => 'example.com', // الجمهور
'iat' => $issuedAt, // تاريخ الإصدار
'exp' => $expirationTime, // انتهاء الصلاحية
'sub' => $userId, // الموضوع (معرف المستخدم)
'data' => [
'userId' => $userId,
'email' => $userEmail
]
];
$jwt = JWT::encode($payload, $secretKey, 'HS256');
// التحقق من JWT
try {
$decoded = JWT::decode($jwt, new Key($secretKey, 'HS256'));
$userData = $decoded->data;
} catch (Exception $e) {
// رمز غير صالح
http_response_code(401);
die('غير مصرح');
}
?>
ثغرات JWT
رموز JWT لها ثغرات محددة يجب معالجتها:
// 1. هجوم الخلط بين الخوارزميات
// ضعيف: السماح بخوارزمية 'none'
$decoded = JWT::decode($jwt, null, ['HS256', 'none']);
// آمن: تحديد الخوارزمية بالضبط
$decoded = JWT::decode($jwt, new Key($secretKey, 'HS256'));
// 2. مفاتيح سرية ضعيفة
// ضعيف: مفتاح يمكن التنبؤ به
$secretKey = 'secret123';
// آمن: مفتاح عشوائي قوي (256+ بت)
$secretKey = bin2hex(random_bytes(32));
// 3. التحقق من انتهاء الصلاحية مفقود
// ضعيف: الرمز لا ينتهي أبدًا
$payload = ['userId' => $userId];
// آمن: قم دائمًا بتعيين انتهاء الصلاحية
$payload = [
'userId' => $userId,
'exp' => time() + 3600
];
// 4. بيانات حساسة في الحمولة
// ضعيف: تخزين كلمات المرور في JWT
$payload = ['password' => $hashedPassword];
// آمن: تخزين المطالبات الضرورية فقط
$payload = ['userId' => $userId, 'role' => $role];
?>
أفضل ممارسات تخزين الرموز
مكان تخزين الرموز يؤثر بشكل كبير على الأمان:
// ❌ LocalStorage - عرضة لهجمات XSS
localStorage.setItem('jwt', token);
// ❌ SessionStorage - عرضة لهجمات XSS
sessionStorage.setItem('jwt', token);
// ✅ ملف تعريف ارتباط HTTP-Only - الأفضل لتطبيقات الويب
<?php
setcookie('jwt', $token, [
'expires' => time() + 3600,
'path' => '/',
'secure' => true,
'httponly' => true,
'samesite' => 'Strict'
]);
?>
// ✅ الذاكرة فقط (SPAs) - مقاوم لـ XSS
// تخزين الرمز في إغلاق JavaScript أو حالة React
// لا تخزن أبدًا في localStorage/sessionStorage
let authToken = null;
function setToken(token) {
authToken = token;
}
function getToken() {
return authToken;
}
تدوير رمز التحديث
قم بتطبيق رموز التحديث لتقليل عمر رمز الوصول مع الحفاظ على جلسات المستخدم:
// وظيفة توليد الرموز
function generateTokenPair($userId) {
$accessTokenExpiry = time() + 900; // 15 دقيقة
$refreshTokenExpiry = time() + 604800; // 7 أيام
$accessToken = JWT::encode([
'sub' => $userId,
'exp' => $accessTokenExpiry,
'type' => 'access'
], getenv('JWT_SECRET'), 'HS256');
$refreshToken = bin2hex(random_bytes(32));
// تخزين رمز التحديث في قاعدة البيانات
storeRefreshToken($userId, $refreshToken, $refreshTokenExpiry);
return [
'access_token' => $accessToken,
'refresh_token' => $refreshToken,
'expires_in' => 900
];
}
// نقطة نهاية التحديث
function refreshAccessToken($refreshToken) {
// التحقق من رمز التحديث في قاعدة البيانات
$tokenData = validateRefreshToken($refreshToken);
if (!$tokenData) {
http_response_code(401);
return ['error' => 'رمز تحديث غير صالح'];
}
// تدوير رمز التحديث (توليد واحد جديد)
invalidateRefreshToken($refreshToken);
// توليد زوج رموز جديد
return generateTokenPair($tokenData['user_id']);
}
// تخزين قاعدة البيانات لرموز التحديث
function storeRefreshToken($userId, $token, $expiry) {
$db->prepare(
'INSERT INTO refresh_tokens (user_id, token, expires_at) ' .
'VALUES (?, ?, FROM_UNIXTIME(?))' .
'ON DUPLICATE KEY UPDATE token = VALUES(token), expires_at = VALUES(expires_at)'
)->execute([$userId, hash('sha256', $token), $expiry]);
}
?>
1. قم بتكوين سمات ملفات تعريف ارتباط الجلسة الآمنة
2. قم بتطبيق إعادة توليد الجلسة عند تسجيل الدخول/الخروج
3. أضف مهلة الجلسة بعد 30 دقيقة من عدم النشاط
4. تحقق من وكيل المستخدم في كل طلب
5. أنشئ وظيفة تسجيل خروج تدمر الجلسات بشكل صحيح
ثم عززه باستخدام JWT:
6. قم بتوليد أزواج رموز الوصول والتحديث
7. قم بتطبيق تدوير الرموز عند التحديث
8. قم بتخزين رموز التحديث بشكل آمن في قاعدة البيانات
9. أضف معالجة انتهاء الصلاحية المناسبة
10. اختبر كلا من تدفقات مصادقة الجلسة و JWT