Trenutno stanje: PHP2Shell. Dvije kritične PHP ranjivosti (CVSS 9.2) omogućuju zlonamjerni kod bez prijave. Zakrpe od 21. 10. 2026.Što sada učiniti →

CSRF zaštita u PHP aplikacijama

Kako razvojni inženjeri u PHP aplikacijama sprječavaju Cross-Site Request Forgery: tokeni za svaki zahtjev koji mijenja podatke, provjera podrijetla u zaglavlju i SameSite kolačići kao dodatni sloj.

Stanje 5 min čitanjaSiguran razvoj

Ukratko: CSRF u PHP-u sprječavate tako da svaki zahtjev koji nešto mijenja mora sa sobom donijeti tajni token vezan uz sesiju, koji na poslužitelju provjeravate pomoću hash_equals(). Uz to provjeravate zaglavlja Origin ili Sec-Fetch-Site i postavljate kolačiće sesije s SameSite. GET zahtjevi nikad ne smiju mijenjati podatke, a i obrazac za prijavu treba zaštitu.

Klik koji nitko ne primijeti

Zaposlenica je prijavljena u backend shopa i u drugoj kartici otvara tuđu stranicu. Ta stranica u pozadini šalje obrazac backendu, primjerice za promjenu adrese e-pošte ili stvaranje računa. Preglednik automatski dodaje kolačić sesije. Aplikaciji zahtjev izgleda kao i svaki drugi iz vlastitog tima.

To je Cross-Site Request Forgery, skraćeno CSRF: zahtjev koji prijavljeni korisnik nesvjesno šalje aplikaciji u koju ima povjerenja. Napadač ne vidi odgovor, ali mu nije ni potreban. Dovoljno je da se radnja izvrši.

CSRF se ne pojavljuje u naslovima kao PHP2Shell, ali je u PHP aplikacijama razvijenim u vlastitoj izvedbi čest nalaz. Frameworci poput Symfonyja i Laravela donose zaštitu; ranjivosti nastaju ponajprije tamo gdje je za pojedine rute isključena. Vlastite izvedbe bez frameworka često je uopće nemaju.

CSRF u PHP-u: koje zahtjeve treba zaštititi

Zaštitu trebaju svi zahtjevi koji mijenjaju stanje: spremanje, brisanje, naručivanje, dodjela prava, promjena postavki, upload datoteka, odjava. Pozivi koji samo čitaju ne trebaju token, sve dok doista samo čitaju.

Iz toga proizlazi prvo pravilo: Nikakve promjene putem GET-a. Poveznice poput loeschen.php?id=… tuđe stranice mogu osobito lako pokrenuti, primjerice preko ugrađene slike. Radnje koje mijenjaju pripadaju u POST, PUT, PATCH ili DELETE.

Drugo pravilo: Zaštititi i prijavu. Kod takozvanog login CSRF-a žrtva se neprimjetno prijavi u račun napadača. Ako tamo unese podatke za plaćanje ili dokumente, oni završavaju kod napadača. Token u obrascu za prijavu i novi ID sesije nakon uspješne prijave to sprječavaju.

Uzorak Synchronizer tokena u PHP-u

Za klasične aplikacije sa sesijama OWASP preporučuje uzorak Synchronizer tokena: poslužitelj stvara slučajni token, sprema ga u sesiju i očekuje ga natrag u svakom obrascu koji mijenja podatke. Važno je da se token šalje u obrascu ili u vlastitom zaglavlju, a ne u kolačiću.

<?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') ?>">
    <!-- ostala polja -->
</form>

hash_equals() uspoređuje u konstantnom vremenu, pa vrijeme odgovora ne otkriva ništa o ispravnoj vrijednosti. Nakon prijave trebali biste obnoviti ID sesije pomoću session_regenerate_id(true) i stvoriti novi token. Zašto je random_bytes() ispravan izvor i koje su funkcije neprikladne, objašnjava članak Sigurni slučajni brojevi i tokeni u PHP-u.

Jedan token po sesiji dovoljan je za većinu aplikacija. Tokeni po obrascu ili po zahtjevu malo povećavaju zaštitu, ali dovode do problema s više otvorenih kartica i gumbom za povratak. Važnije od učestalosti promjene je da se token nikad ne pojavljuje u URL-ovima, gdje može završiti u zapisnicima, povijesti preglednika i u podacima o referreru prema tuđim stranicama.

Za aplikacije bez stanja i bez sesije OWASP kao alternativu navodi potpisani Double-Submit kolačić: token vezan za sesiju pomoću HMAC-a. Jednostavna, nepotpisana varijanta izričito se ne preporučuje.

Drugi sloj: Origin, Sec-Fetch-Site i SameSite

Moderni preglednici pri zahtjevima šalju podatke o svojem podrijetlu. Zaglavlje Origin navodi stranicu koja poziva, a Sec-Fetch-Site kaže dolazi li zahtjev s iste stranice (same-origin), srodne (same-site) ili tuđe (cross-site). Iz toga se može sastaviti jednostavna dodatna provjera:

<?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 pritom naglašava dvije točke: podrijetlo se mora uspoređivati u cijelosti kako domene sličnog zvuka ne bi prošle, a nedostajuće zaglavlje ili vrijednost null ne smiju vrijediti kao vlastito podrijetlo. Budući da stariji preglednici zaglavlja ne šalju uvijek, token ostaje glavna obrana.

Treći sloj su kolačići sesije s SameSite. PHP podržava session.cookie_samesite od verzije 7.3, a zadana vrijednost je prazna. Postavite najmanje Lax, za čiste backende često je moguć Strict. Ostale postavke kolačića opisuje vodič Osigurati sesije u PHP-u.

Ako koristite CMS, on obično donosi vlastite alate. WordPress, primjerice, radi s takozvanim nonceovima, koje dodaci uključuju funkcijama poput wp_nonce_field() i check_admin_referer(). Ranjivosti u praksi nastaju ponajprije u proširenjima koja takve provjere zaborave kod pojedinih radnji. Zato osobito provjerite vlastoručno napisane ili malo raširene dodatke.

Kod sučelja koja preglednici koriste s drugih domena dodatno odlučuje CORS konfiguracija. Ako poslužitelj svako podrijetlo koje šalje zahtjev vraća kao dopušteno, a istodobno dopušta kolačiće, sam poništava zaštitu preglednika. OWASP preporučuje CORS otvoriti samo za izričito pouzdana podrijetla; paušalno odobrenje svih poddomena postaje problem čim jedna od njih dospije u tuđe ruke.

Kontrolni popis za postojeće aplikacije

Kod razvijenih projekata pomaže sustavan pregled:

  1. Popisati sve rute i skripte koje mijenjaju podatke, uključujući Ajax krajnje točke i uploade datoteka.
  2. Provjeriti koristi li se za to GET i prijeći na POST ili odgovarajuće metode.
  3. Za svaku rutu koja mijenja dokazati da se token provjerava; iznimke u frameworku tražiti ciljano.
  4. Zasebno testirati prijavu, odjavu i promjenu lozinke.
  5. Dopuniti session.cookie_samesite i provjeru podrijetla.
  6. Ukloniti XSS ranjivosti, jer ubačeni kod može pročitati svaki token. Postupak opisuje članak Spriječiti Cross-Site Scripting u PHP-u.
Važno

CSRF zaštita koja se provjerava samo u frontendu pomoću JavaScripta nema učinka. Provjera se mora odvijati na poslužitelju, prije izvršenja radnje.

Kako PHP SOS konkretno pomaže?

  • Bilježimo sve rute Vaše PHP aplikacije koje mijenjaju podatke i provjeravamo gdje token nedostaje ili je isključen.
  • Ugrađujemo središnju točku provjere umjesto da se provjere tokena raspoređuju po mnogim skriptama.
  • Dopunjujemo provjeru podrijetla i SameSite postavke, a da ne smetamo procesima plaćanja ili prijave.
  • Nakon prijelaza ponovno testiramo i dokumentiramo rezultate.

Potpunu provjeru Vaše aplikacije opisuje usluga Sigurnosni audit i penetracijski test.

Česta pitanja

Je li SameSite=Lax dovoljan kao CSRF zaštita?

Ne. SameSite kolačići prema OWASP-u su dodatni sloj, a ne zamjena. Lax i dalje šalje kolačiće pri običnim pozivima stranica metodom GET, a napadi s poddomena iste web-stranice nisu pokriveni.

Trebaju li JSON API-ji CSRF token?

Ako API autentificira pomoću kolačića, da, ili jednakovrijednu provjeru. Alternativa su vlastita zaglavlja zahtjeva koja preglednici s tuđih stranica dopuštaju tek nakon CORS prethodne provjere. Tada CORS odobrenje smije sadržavati samo izričito pouzdana podrijetla.

Mora li se zaštititi i obrazac za prijavu?

Da. Kod login CSRF-a žrtva se neprimjetno prijavi u tuđi račun i tamo unosi vlastite podatke. Token u obrascu za prijavu i nova sesija nakon prijave to sprječavaju.

Štiti li CSRF zaštita i od XSS-a?

Obrnuto: XSS ranjivost poništava svaku CSRF zaštitu, jer ubačeni kod u ime stranice može pročitati valjane tokene i poslati ih. Obje se ranjivosti moraju ukloniti zasebno.

Izvori

Hitni kontakt

Svaki sat se računa. Pišite nam odmah.

Ukratko opišite sustav, verziju PHP-a i opažanje. Javit ćemo Vam se, razjasniti situaciju i raditi na daljinu dok incident nije zaključen i dokumentiran.

Hitna pomoć