Īsi sakot: CSRF PHP novērš, panākot, ka katram pieprasījumam, kas ko maina, jānes līdzi slepens, ar sesiju saistīts tokens, ko pārbaudāt servera pusē ar hash_equals(). Papildus pārbaudiet galvenes Origin vai Sec-Fetch-Site un sesijas sīkdatnēm iestatiet SameSite. GET pieprasījumi nekad nedrīkst mainīt datus, un pieteikšanās veidlapai arī vajadzīga aizsardzība.
Klikšķis, ko neviens nepamana
Darbiniece ir pieteikusies veikala aizmugurē un otrā cilnē atver svešu lapu. Šī lapa fonā nosūta aizmugurei veidlapu, piemēram, e-pasta adreses maiņai vai konta izveidei. Pārlūks sesijas sīkdatni pievieno automātiski. Lietojumprogrammai pieprasījums izskatās kā jebkurš cits no savas komandas.
Tas ir Cross-Site Request Forgery, saīsināti CSRF: pieprasījums, ko pieteicies lietotājs nezinot nosūta lietojumprogrammai, kurai uzticas. Uzbrucējs atbildi neredz, bet tā viņam nav vajadzīga. Pietiek, ka darbība tiek izpildīta.
CSRF nav virsrakstos kā PHP2Shell, taču pašu izstrādātās PHP lietojumprogrammās tas ir biežs konstatējums. Ietvari, piemēram, Symfony un Laravel, nāk ar aizsardzību; nepilnības rodas galvenokārt tur, kur tā atsevišķiem maršrutiem izslēgta. Pašizstrādājumiem bez ietvara bieži nav nekādas.
CSRF PHP: kuri pieprasījumi jāaizsargā
Aizsardzība vajadzīga visiem pieprasījumiem, kas maina stāvokli: saglabāšana, dzēšana, pasūtīšana, tiesību piešķiršana, iestatījumu maiņa, failu augšupielāde, atteikšanās. Lasošiem izsaukumiem tokens nav vajadzīgs, kamēr tie tiešām tikai lasa.
No tā izriet pirmais noteikums: nekādu izmaiņu ar GET. Tādas saites kā loeschen.php?id=… svešas lapas var izraisīt īpaši viegli, piemēram, ar iegultu attēlu. Mainošām darbībām jānotiek ar POST, PUT, PATCH vai DELETE.
Otrais noteikums: aizsargāt arī pieteikšanos. Tā saucamajā Login-CSRF upuris nemanot tiek pieteikts uzbrucēja kontā. Ja viņš tur ievada maksājumu datus vai dokumentus, tie nonāk pie uzbrucēja. To novērš tokens pieteikšanās veidlapā un jauns sesijas ID pēc sekmīgas pieteikšanās.
Synchronizer token paraugs PHP
Klasiskām lietojumprogrammām ar sesijām OWASP iesaka Synchronizer token paraugu: serveris izveido nejaušu tokenu, saglabā to sesijā un sagaida to atpakaļ katrā mainošā veidlapā. Svarīgi, ka tokens tiek sūtīts veidlapā vai savā galvenē, nevis sīkdatnē.
<?php
declare(strict_types=1);
function csrfToken(): string
{
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
return $_SESSION['csrf_token'];
}
function csrfPruefen(): void
{
$gesendet = $_POST['csrf_token'] ?? $_SERVER['HTTP_X_CSRF_TOKEN'] ?? '';
if (!is_string($gesendet) || !hash_equals(csrfToken(), $gesendet)) {
http_response_code(403);
exit('Anfrage abgelehnt.');
}
}
if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
csrfPruefen();
}
?>
<form method="post" action="/konto/email">
<input type="hidden" name="csrf_token"
value="<?= htmlspecialchars(csrfToken(), ENT_QUOTES, 'UTF-8') ?>">
<!-- citi lauki -->
</form>
hash_equals() salīdzina nemainīgā laikā, lai atbildes laiks neizpauž pareizo vērtību. Pēc pieteikšanās sesijas ID atjaunojiet ar session_regenerate_id(true) un izveidojiet jaunu tokenu. Kāpēc random_bytes() ir pareizais avots un kuras funkcijas nav piemērotas, skaidro Droši gadījuma skaitļi un tokeni PHP.
Vairumam lietojumprogrammu pietiek ar vienu tokenu sesijā. Tokeni katrai veidlapai vai katram pieprasījumam aizsardzību nedaudz palielina, bet rada problēmas ar vairākām atvērtām cilnēm un pogu Atpakaļ. Svarīgāk par maiņas biežumu ir tas, ka tokens nekad neparādās URL, kur tas var nonākt žurnālos, pārlūka vēsturē un Referrer norādēs svešām lapām.
Bezstāvokļa lietojumprogrammām bez sesijas OWASP kā alternatīvu min parakstīto Double-Submit Cookie: tokenu, kas ar HMAC piesaistīts sesijai. Vienkāršais, neparakstītais variants tiek nepārprotami neieteikts.
Otrs slānis: Origin, Sec-Fetch-Site un SameSite
Mūsdienu pārlūki pieprasījumos sūta informāciju par savu izcelsmi. Galvene Origin nosauc izsaucošo lapu, Sec-Fetch-Site norāda, vai pieprasījums nāk no tās pašas vietnes (same-origin), radniecīgas (same-site) vai svešas (cross-site). No tā var izveidot vienkāršu papildu pārbaudi:
<?php
$erlaubteHerkunft = 'https://www.example.de';
$fetchSite = $_SERVER['HTTP_SEC_FETCH_SITE'] ?? null;
$origin = $_SERVER['HTTP_ORIGIN'] ?? null;
$verdaechtig = ($fetchSite !== null && $fetchSite !== 'same-origin')
|| ($fetchSite === null && $origin !== null && $origin !== $erlaubteHerkunft);
if ($_SERVER['REQUEST_METHOD'] !== 'GET' && $verdaechtig) {
http_response_code(403);
exit;
}
OWASP uzsver divus punktus: izcelsme jāsalīdzina pilnībā, lai līdzīgi skanoši domēni neizslīdētu cauri, un trūkstoša galvene vai vērtība null nedrīkst skaitīties par savu izcelsmi. Tā kā vecāki pārlūki galvenes ne vienmēr sūta, tokens paliek galvenā aizsardzība.
Trešais slānis ir sesijas sīkdatnes ar SameSite. PHP session.cookie_samesite atbalsta kopš versijas 7.3, noklusējuma vērtība ir tukša. Iestatiet vismaz Lax, tīrām aizmugurēm bieži iespējams Strict. Pārējos sīkdatņu iestatījumus apraksta ceļvedis Sesiju nostiprināšana PHP.
Ja lietojat CMS, tā parasti nāk ar saviem rīkiem. WordPress, piemēram, strādā ar tā sauktajiem nonces, ko spraudņi iekļauj ar funkcijām, piemēram, wp_nonce_field() un check_admin_referer(). Praksē nepilnības rodas galvenokārt paplašinājumos, kas šādas pārbaudes atsevišķām darbībām aizmirst. Tāpēc īpaši pārbaudiet pašrakstītus vai maz izplatītus spraudņus.
Saskarnēm, ko pārlūki izmanto no citiem domēniem, papildus izšķir CORS konfigurācija. Ja serveris katru pieprasījuma izcelsmi atdod kā atļautu un vienlaikus pieļauj sīkdatnes, tas pats izslēdz pārlūka aizsardzību. OWASP iesaka CORS atvērt tikai nepārprotami uzticamām izcelsmēm; vispārēja visu apakšdomēnu atļauja kļūst par problēmu, tiklīdz kāds no tiem nonāk svešās rokās.
Pārbaudes saraksts esošām lietojumprogrammām
Izaugušos projektos palīdz sistemātiska caurskate:
- Apkopot visus maršrutus un skriptus, kas maina datus, arī Ajax galapunktus un failu augšupielādes.
- Pārbaudīt, vai tiem tiek lietots GET, un pāriet uz POST vai piemērotām metodēm.
- Katram mainošam maršrutam pierādīt, ka tokens tiek pārbaudīts; izņēmumus ietvarā meklēt mērķtiecīgi.
- Atsevišķi pārbaudīt pieteikšanos, atteikšanos un paroles maiņu.
- Papildināt
session.cookie_samesiteun izcelsmes pārbaudi. - Novērst XSS ievainojamības, jo ievadīts kods var nolasīt katru tokenu. Rīcību apraksta Cross-Site Scripting novēršana PHP.
CSRF aizsardzība, ko pārbauda tikai priekšpusē ar JavaScript, ir bezspēcīga. Pārbaudei jānotiek serverī, pirms darbība tiek izpildīta.
Kā PHP SOS palīdz konkrēti?
- Mēs apkopojam visus jūsu PHP lietojumprogrammas mainošos maršrutus un pārbaudām, kur tokena trūkst vai tas ir izslēgts.
- Mēs ieviešam centrālu pārbaudes punktu, nevis izkaisām tokenu pārbaudes pa daudziem skriptiem.
- Mēs papildinām izcelsmes pārbaudi un SameSite iestatījumus, netraucējot maksājumu vai pieteikšanās procesus.
- Pēc pārejas mēs testējam vēlreiz un dokumentējam rezultātus.
Pilnu jūsu lietojumprogrammas pārbaudi apraksta pakalpojums drošības audits un penetrācijas tests.
Biežāk uzdotie jautājumi
Vai SameSite=Lax pietiek kā CSRF aizsardzība?
Nē. Saskaņā ar OWASP SameSite sīkdatnes ir papildu slānis, nevis aizstājējs. Lax sīkdatnes joprojām sūta parastos lapu izsaukumos ar GET, un uzbrukumi no tās pašas vietnes apakšdomēniem netiek aptverti.
Vai JSON API vajadzīgs CSRF tokens?
Ja API autentificē ar sīkdatnēm, jā vai līdzvērtīga pārbaude. Alternatīva ir savas pieprasījuma galvenes, ko pārlūki svešām lapām atļauj tikai pēc CORS pirmspārbaudes. Tad CORS atļaujā tomēr drīkst būt tikai nepārprotami uzticamas izcelsmes.
Vai jāaizsargā arī pieteikšanās veidlapa?
Jā. Login-CSRF gadījumā upuris nemanot tiek pieteikts svešā kontā un tur ievada savus datus. To novērš tokens pieteikšanās veidlapā un jauna sesija pēc pieteikšanās.
Vai CSRF aizsardzība pasargā arī no XSS?
Otrādi: XSS ievainojamība izslēdz jebkuru CSRF aizsardzību, jo ievadīts kods lapas vārdā var nolasīt derīgus tokenus un tos nosūtīt līdzi. Abas vājās vietas jānovērš atsevišķi.