Protection CSRF (Origin + jeton scopé)
لماذا حماية المشاركات
بدون رمز CSRF المرتبط بالجلسة، يمكن لموقع جهة خارجية إرسال نموذج POST إلى PmaControl بينما يتصفح المستخدم المعتمد في مكان آخر. يقوم المتصفح تلقائيًا بإرفاق ملفات تعريف الارتباط للجلسة ويتم تنفيذ الإجراء دون نية المستخدم - تعديل التكوين، وإطلاق العملية، والتحديث المضمن.
Glial المساعد المشترك
من Glial v5.1.40، تتم مشاركة منطق CSRF والتحقق من صحة المنشأ/المرجع في إطار العمل، وبالتالي تتم مشاركته بواسطة جميع وحدات التحكم PmaControl:
Glial\Security\Csrf::issueToken()/Csrf::validateToken()— إصدار الرمز المميز والتحقق منه حسب النطاق الوظيفي.Glial\Http\Request::isSameSite()— مقارنة الأصل (الأولوية) ثم المُحيل (الاحتياطي) مقابل الأصل الحالي.
Worker.php يحتوي فقط على تنسيق HTTP الخاص بـ POST /Worker/update.مخطط التدفق
إصدار الرمز المميز
قبل تقديم طريقة عرض تحتوي على نموذج أو خلية قابلة للتحرير، تقوم وحدة التحكم بإصدار رمز مميز على نطاق الميزة:
<?php
use Glial\Security\Csrf;
$token = Csrf::issueToken($_SESSION, 'worker.update');
$this->set('csrf_token', $token);
worker.update على daemon.start. يتم تخزين الرمز المميز في $_SESSION['csrf_tokens'][$scope] ويستمر حتى نهاية جلسة المستخدم.الحقن من جانب العميل
بالنسبة للخلايا القابلة للتحرير المضمّن (قابلة للتحرير)، يتم إدخال الرمز المميز عبر سمتين data-*:
<td class="editable"
data-name="nb_worker"
data-csrf-field="csrf_token"
data-csrf-token="<?= htmlspecialchars($csrf_token, ENT_QUOTES, 'UTF-8') ?>">
5
</td>
htmlspecialchars(..., ENT_QUOTES, 'UTF-8') لتحييد أي حقن HTML/JS في الخلية.يكتشف App/Webroot/js/Tree/index.js JS وجود data-csrf-token ويضيف الرمز المميز تلقائيًا إلى معلمات POST المرسلة بواسطة bootstrap-editable:
$.fn.editable.defaults.params = function (params) {
var $cell = $(this).closest('[data-csrf-token]');
if ($cell.length) {
params[$cell.data('csrf-field')] = $cell.data('csrf-token');
}
return params;
};
window.pmacontrolInitLineEdit(context) مرة أخرى في السياق المعاد تحميله لربط الرمز المميز الجديد بالخلايا الجديدة.سلسلة التحقق من جانب الخادم
أربع عمليات تحقق تتبع بعضها البعض بهذا الترتيب الدقيق، قبل أي بناء SQL:
- طريقة HTTP —
POSTفقط؛ أي طريقة أخرى ترجع405 Method Not Allowed. - أصل الموقع نفسه —
Originفي الأولوية، ثمRefererفي الإجراء الاحتياطي؛ تم رفض القيمnull، المرتبطة بالبروتوكول أو ذات المخطط/المضيف/المنفذ المختلف مع403 Invalid request origin. - رمز CSRF — مقارنة
hash_equals()بين حقل الحمولةcsrf_tokenو$_SESSION['csrf_tokens'][$scope]؛ يقوم الرمز المفقود أو غير الصالح بإرجاع403 Invalid CSRF token. - حمولة القائمة المسموح بها - الحقول والأنواع المخصصة لنقطة النهاية هذه فقط؛ حمولة غير قائمة ترجع
400 Invalid worker update payload.
<?php
class Worker
{
public function update()
{
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
return;
}
if (!Glial\Http\Request::isSameSite($_SERVER)) {
http_response_code(403);
echo 'Invalid request origin';
return;
}
if (!Glial\Security\Csrf::validateToken($_POST, $_SESSION, 'worker.update')) {
http_response_code(403);
echo 'Invalid CSRF token';
return;
}
$sql = $this->buildWorkerUpdateSql($_POST);
if ($sql === null) {
http_response_code(400);
echo 'Invalid worker update payload';
return;
}
// SQL exécuté ici uniquement
}
}
المرجع - رموز الإرجاع
| شفرة | سبب | متى |
|---|---|---|
405 |
Method Not Allowed | طريقة HTTP بخلاف POST. |
403 |
Invalid request origin | أصل/مرجع خارج الموقع، Origin: null، مرتبط بالبروتوكول أو مخطط/مضيف/منفذ مختلف. |
403 |
Invalid CSRF token | الرمز المميز مفقود من الحمولة أو لا يتوافق مع $_SESSION['csrf_tokens'][$scope]. |
400 |
Invalid worker update payload | حقل خارج القائمة المسموح بها، قيمة غير صحيحة، pk سالبة أو فارغة. |
الإعفاءات الموثقة
يغطي مساعد CSRF منشورات الويب فقط. الأسطح التالية معفاة صراحة:
- CLI
php glial …- لا يوجد ملف تعريف ارتباط للجلسة، ولا يوجد سطح CSRF. - REST API من جهاز إلى جهاز — مصادقة رمزية منفصلة لواجهة برمجة التطبيقات؛ تم تعطيل التحكم في الأصل/المرجع لهذه المسارات ويتم توثيق الاستثناء لكل نقطة نهاية.
- نقاط النهاية للقراءة فقط - تم تحويلها إلى GET، وبالتالي فهي خارج نطاق CSRF.
اختبارات مخصصة
تتم تغطية حماية CSRF بواسطة اختبارات PHPUnit التي تجسد بوضوح سيناريو الهجوم التاريخي:
./vendor/bin/phpunit tests/Glial/Security/CsrfTest.php
./vendor/bin/phpunit tests/Glial/Http/RequestTest.php
./vendor/bin/phpunit tests/Controller/WorkerUpdateSecurityTest.php
Origin: https://attacker.test أو أي موقع خارجي آخر، يتم رفض الحمولة في 403 Invalid request origin قبل أي كتابة، حتى لو تم توفير رمز CSRF صالح — ترفض سلسلة التحقق الطلب الموجود على عنصر تحكم الأصل قبل الوصول إلى الرمز المميز.