# CSRF-védelem PHP-alkalmazásokban

> CSRF-védelem PHP-ban: synchronizer token, az Origin és a Sec-Fetch-Site ellenőrzése, SameSite sütik és login-CSRF. Kódpéldával és ellenőrzőlistával.

Forrás: https://phpsos.de/hu/blog/csrf-schutz-php/  
Állapot: 2026. 10. 25.  
Szolgáltató: PHP SOS, PHP-vészhelyzeti segítség és szerverbiztonság. Azonnali segítség WhatsAppon: https://wa.me/491736494914?text=%C3%9Cdv%C3%B6zl%C3%B6m%2C%20PHP%20SOS%21%20Seg%C3%ADts%C3%A9gre%20van%20sz%C3%BCks%C3%A9g%C3%BCnk%20a%20PHP-szerver%C3%BCnkkel.%20C%C3%A9g%3A%20%E2%80%A6%20Weboldal%3A%20%E2%80%A6

**Röviden:** A CSRF-et PHP-ban úgy előzi meg, hogy minden olyan kérésnek, amely valamit módosít, egy titkos, a sessionhöz kötött tokent kell magával hoznia, amelyet a szerver oldalán a `hash_equals()` függvénnyel ellenőriz. Emellett ellenőrzi az `Origin` vagy a `Sec-Fetch-Site` fejlécet, és a session-sütiket `SameSite` beállítással látja el. A GET-kérések soha nem módosíthatnak adatot, és a bejelentkezési űrlapnak is védelem kell.

## Egy kattintás, amelyet senki sem vesz észre

Egy munkatársnő be van jelentkezve a webshop backendjébe, és egy második lapon megnyit egy idegen oldalt. Ez az oldal a háttérben űrlapot küld a backendnek, például egy e-mail-cím módosításához vagy egy fiók létrehozásához. A böngésző automatikusan hozzáfűzi a session-sütit. Az alkalmazás számára a kérés úgy néz ki, mint bármelyik másik a saját csapatból.

Ez a Cross-Site Request Forgery, röviden CSRF: egy kérés, amelyet a bejelentkezett felhasználó tudtán kívül küld egy olyan alkalmazásnak, amelyben megbízik. A támadó nem látja a választ, de nincs is rá szüksége. Elég, hogy a művelet végrehajtódik.

A CSRF nem szerepel a címlapokon, mint a PHP2Shell, de a saját fejlesztésű PHP-alkalmazásokban gyakori megállapítás. Az olyan keretrendszerek, mint a Symfony és a Laravel, védelmet hoznak magukkal; a rések ott főleg ott keletkeznek, ahol egyes útvonalaknál kikapcsolták. A keretrendszer nélküli saját fejlesztéseknél gyakran nincs is.

## CSRF PHP-ban: mely kéréseket kell védeni

Védelmet igényel minden olyan kérés, amely állapotot változtat: mentés, törlés, rendelés, jogok kiosztása, beállítások módosítása, fájlok feltöltése, kijelentkezés. Az olvasó hívások nem igényelnek tokent, amíg valóban csak olvasnak.

Ebből következik az első szabály: **Nincs módosítás GET-tel.** Az olyan linkeket, mint a `loeschen.php?id=…`, idegen oldalak különösen könnyen kiválthatják, például egy beágyazott képen keresztül. A módosító műveletek POST, PUT, PATCH vagy DELETE metódusba valók.

A második szabály: **A bejelentkezést is védeni kell.** Az úgynevezett login-CSRF esetén az áldozatot észrevétlenül a támadó egy fiókjába léptetik be. Ha ott fizetési adatokat vagy dokumentumokat ad meg, azok a támadóhoz kerülnek. A bejelentkezési űrlapban lévő token és a sikeres bejelentkezés utáni új session-azonosító ezt megakadályozza.

## A synchronizer token minta PHP-ban

A munkamenetekkel dolgozó klasszikus alkalmazásokhoz az OWASP a synchronizer token mintát ajánlja: a szerver véletlenszerű tokent generál, elmenti a sessionben, és minden módosító űrlapban visszavárja. Fontos, hogy a tokent az űrlapban vagy egy saját fejlécben küldjék el, nem sütiben.

```php
<?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') ?>">
    <!-- további mezők -->
</form>
```

A `hash_equals()` állandó időben hasonlít, így a válaszidő semmit sem árul el a helyes értékről. A bejelentkezés után a session-azonosítót újítsa meg a `session_regenerate_id(true)` függvénnyel, és hozzon létre új tokent. Hogy a `random_bytes()` miért a helyes forrás, és mely függvények alkalmatlanok, azt a [Biztonságos véletlenszámok és tokenek PHP-ban](https://phpsos.de/hu/blog/sichere-tokens-php/) magyarázza el.

A legtöbb alkalmazásnál elég egy token sessionönként. Az űrlaponkénti vagy kérésenkénti tokenek kissé növelik a védelmet, de gondokat okoznak több nyitott lapnál és a vissza gombnál. A váltás gyakoriságánál fontosabb, hogy a token soha ne jelenjen meg URL-ekben, ahol naplókba, a böngészőelőzményekbe és a Referrer-adatokon keresztül idegen oldalakhoz kerülhet.

A munkamenet nélküli, állapotmentes alkalmazásokhoz az OWASP alternatívaként az aláírt double-submit sütit említi: egy tokent, amelyet HMAC köt a munkamenethez. Az egyszerű, aláíratlan változatot kifejezetten nem ajánlja.

## Második réteg: Origin, Sec-Fetch-Site és SameSite

A modern böngészők a kéréseknél információt küldenek az eredetükről. Az `Origin` fejléc megnevezi a hívó oldalt, a `Sec-Fetch-Site` pedig megmondja, hogy a kérés ugyanarról az oldalról (`same-origin`), egy rokon oldalról (`same-site`) vagy egy idegenről (`cross-site`) származik-e. Ebből egyszerű kiegészítő ellenőrzés építhető:

```php
<?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;
}
```

Az OWASP itt két pontot hangsúlyoz: az eredetet teljes egészében kell összehasonlítani, hogy a hasonlóan hangzó domainek ne csússzanak át, és egy hiányzó fejléc vagy a `null` érték nem számíthat saját eredetnek. Mivel a régebbi böngészők nem mindig küldik el a fejléceket, a token marad a fő védelem.

A harmadik réteg a `SameSite` beállítású session-sütik. A PHP a `session.cookie_samesite` beállítást a 7.3-as verzió óta támogatja, az alapértelmezett érték üres. Állítson be legalább `Lax` értéket, tisztán backendeknél gyakran a `Strict` is lehetséges. A többi süti-beállítást a [A sessionök biztonságossá tétele PHP-ban](https://phpsos.de/hu/ratgeber/session-sicherheit-php/) útmutató írja le.

Ha CMS-t használ, az többnyire saját eszközöket hoz magával. A WordPress például úgynevezett noncekkal dolgozik, amelyeket a bővítmények olyan függvényekkel építenek be, mint a `wp_nonce_field()` és a `check_admin_referer()`. A gyakorlatban a rések főleg azokban a bővítményekben keletkeznek, amelyek egyes műveleteknél elfelejtik ezeket az ellenőrzéseket. Ezért különösen a saját írású vagy kevéssé elterjedt bővítményeket ellenőrizze.

A más domainekről a böngészőből használt interfészeknél emellett a CORS-konfiguráció dönt. Ha a szerver minden kérő eredetet engedélyezettként ad vissza, és közben sütiket is megenged, maga iktatja ki a böngésző védelmét. Az OWASP azt ajánlja, hogy a CORS-t csak kifejezetten megbízható eredetekre nyissák meg; az összes aldomain átalánydíjas engedélyezése akkor válik problémává, ha valamelyik idegen kézbe kerül.

## Ellenőrzőlista meglévő alkalmazásokhoz

Gyarapodott projekteknél a szisztematikus átvizsgálás segít:

1. Felvenni az összes útvonalat és szkriptet, amely adatot módosít, az Ajax-végpontokat és a fájlfeltöltéseket is.
2. Ellenőrizni, hogy használnak-e hozzá GET-et, és átállítani POST-ra vagy megfelelő metódusokra.
3. Minden módosító útvonalnál bizonyítani, hogy tokent ellenőriznek; a keretrendszer kivételeit célzottan keresni.
4. A bejelentkezést, a kijelentkezést és a jelszómódosítást külön tesztelni.
5. A `session.cookie_samesite` beállítást és az eredetellenőrzést kiegészíteni.
6. Megszüntetni az XSS-réseket, mert a beinjektált kód bármely tokent elolvashat. Az eljárást a [Cross-site scripting megelőzése PHP-ban](https://phpsos.de/hu/ratgeber/xss-php/) írja le.

> **Fontos:**
> Az a CSRF-védelem, amelyet csak a frontenden JavaScripttel ellenőriznek, hatástalan. Az ellenőrzésnek a szerveren kell megtörténnie, mielőtt a művelet végrehajtódik.

## Hogyan segít konkrétan a PHP SOS?

- Felmérjük a PHP-alkalmazása összes módosító útvonalát, és ellenőrizzük, hol hiányzik vagy van kikapcsolva a token.
- Központi ellenőrzési pontot építünk be ahelyett, hogy a tokenellenőrzéseket sok szkriptre szórnánk szét.
- Kiegészítjük az eredetellenőrzést és a SameSite-beállításokat anélkül, hogy a fizetési vagy bejelentkezési folyamatokat zavarnánk.
- Az átállítás után újra tesztelünk, és dokumentáljuk az eredményeket.

Az alkalmazás teljes körű ellenőrzését a [biztonsági audit és penetrációs teszt](https://phpsos.de/hu/leistungen/sicherheitsaudit-penetrationstest/) szolgáltatás írja le.


## Gyakori kérdések

**Elég a SameSite=Lax CSRF-védelemként?**  
Nem. Az OWASP szerint a SameSite sütik további réteget jelentenek, nem pótlást. A Lax a sütiket továbbra is elküldi a normál, GET-tel történő oldalmegnyitásoknál, és ugyanazon weboldal aldomainjeiről érkező támadásokat nem fedi le.

**Kell CSRF-token a JSON-API-khoz?**  
Ha az API sütikkel hitelesít, igen, vagy ezzel egyenértékű ellenőrzés. Alternatíva a saját kérés-fejlécek használata, amelyeket a böngészők idegen oldalaknál csak CORS-előellenőrzés után engednek meg. Ilyenkor a CORS-engedélybe viszont csak kifejezetten megbízható eredetek kerülhetnek.

**A bejelentkezési űrlapot is védeni kell?**  
Igen. A login-CSRF esetén az áldozatot észrevétlenül egy idegen fiókba léptetik be, és ott a saját adatait adja meg. A bejelentkezési űrlapban lévő token és a bejelentkezés utáni új session ezt megakadályozza.

**Véd a CSRF-védelem az XSS ellen is?**  
Fordítva: egy XSS-rés minden CSRF-védelmet kijátszik, mert a beinjektált kód az oldal nevében érvényes tokeneket olvashat és küldhet el. Mindkét sebezhetőséget külön kell megszüntetni.

## Források

- [OWASP Cross-Site Request Forgery Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html)
- [PHP kézikönyv: session-konfiguráció](https://www.php.net/manual/de/session.configuration.php)
- [OWASP Top 10](https://owasp.org/www-project-top-ten/)
