Protection CSRF (Origin + jeton scopé)
Dlaczego chronić POST
Bez tokenu CSRF powiązanego z sesją, zewnętrzna witryna może wysłać POST do PmaControl, gdy uwierzytelniony użytkownik przegląda inną stronę. Przeglądarka automatycznie dołącza cookies sesji, a akcja wykonuje się bez intencji użytkownika — zmiana konfiguracji, uruchomienie operacji, edycja inline.
Wspólny helper Glial
Od Glial v5.1.40 logika CSRF i walidacja Origin/Referer znajdują się we frameworku i są wspólne dla wszystkich kontrolerów PmaControl:
Glial\Security\Csrf::issueToken()/Csrf::validateToken()— wydawanie i walidacja tokenu na scope funkcjonalny.Glial\Http\Request::isSameSite()— porównuje Origin (priorytet), następnie Referer (fallback) z bieżącym origin.
Worker.php zawiera tylko orkiestrację HTTP właściwą dla POST /Worker/update.Schemat przepływu
Wydanie tokenu
Przed wyrenderowaniem widoku z formularzem lub komórką inline-editable kontroler wydaje token z określonym scope:
<?php
use Glial\Security\Csrf;
$token = Csrf::issueToken($_SESSION, 'worker.update');
$this->set('csrf_token', $token);
worker.update na daemon.start. Token jest przechowywany w $_SESSION['csrf_tokens'][$scope] i trwa do końca sesji użytkownika.Wstrzyknięcie po stronie klienta
Dla komórek inline-editable (bootstrap-editable) token jest wstrzykiwany przez dwa atrybuty 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'), aby zneutralizować jakąkolwiek injekcję HTML/JS w komórce.JS App/Webroot/js/Tree/index.js wykrywa obecność data-csrf-token i automatycznie dołącza token do parametrów POST wysyłanych przez 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) jest wywoływane ponownie dla przeładowanego kontekstu, aby powiązać nowy token z nowymi komórkami.Łańcuch walidacji po stronie serwera
Cztery kontrole wykonywane są w tej dokładnej kolejności, przed jakąkolwiek konstrukcją SQL:
- Metoda HTTP — wyłącznie
POST; każda inna zwraca405 Method Not Allowed. - Origin same-site — najpierw
Origin, potemRefererjako fallback; wartościnull, protocol-relative oraz inny scheme/host/port są odrzucane z403 Invalid request origin. - Token CSRF — porównanie
hash_equals()pomiędzy polemcsrf_tokenw payload i$_SESSION['csrf_tokens'][$scope]; brakujący lub błędny token zwraca403 Invalid CSRF token. - Allowlist payload — wyłącznie pola i typy przewidziane dla tego endpointu; payload spoza listy zwraca
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
}
}
Referencja — kody zwrotne
| Kod | Powód | Kiedy |
|---|---|---|
405 |
Method Not Allowed | Metoda HTTP inna niż POST. |
403 |
Invalid request origin | Origin/Referer spoza witryny, Origin: null, protocol-relative lub inny scheme/host/port. |
403 |
Invalid CSRF token | Token brak w payload lub nie pasuje do $_SESSION['csrf_tokens'][$scope]. |
400 |
Invalid worker update payload | Pole spoza allowlist, wartość nie-całkowita, ujemny lub zerowy pk. |
Udokumentowane wyjątki
Helper CSRF obejmuje wyłącznie POST webowe. Następujące powierzchnie są wyraźnie wyłączone:
- CLI
php glial …— brak cookie sesji, brak powierzchni CSRF. - API REST machine-to-machine — uwierzytelnianie osobnym tokenem API; kontrola Origin/Referer jest wyłączona dla tych tras, a wyjątek jest udokumentowany na poziomie endpointu.
- Endpointy read-only — przeniesione na GET, czyli poza zakresem CSRF.
Dedykowane testy
Ochrona CSRF jest pokryta testami PHPUnit, które jawnie odwzorowują historyczny scenariusz ataku:
./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 lub innej witryny zewnętrznej payload zostaje odrzucony z 403 Invalid request origin przed jakimkolwiek zapisem, nawet jeśli dostarczony jest poprawny token CSRF — łańcuch walidacji zatrzymuje żądanie na kontroli Origin przed dotarciem do tokenu.