# Type juggling PHP-ban: laza összehasonlítások mint biztonsági kockázat

> A PHP type juggling megértése: hogyan hasonlít a ==, mit változtatott a PHP 8.0, és miért kell szigorúan összevetni a tokeneket, hasheket, szerepköröket.

Forrás: https://phpsos.de/hu/blog/type-juggling-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 PHP type juggling az automatikus típusátalakítást jelenti, amely a `==` operátorral végzett laza összehasonlításnál dönti el az eredményt. A PHP 8.0 óta a szabályok szigorúbbak, de a numerikus karakterláncokat továbbra is számként hasonlítja össze, a `true`, `false` és `null` pedig saját szabályokat követ. A biztonsági szempontból fontos ellenőrzésekre ezért ez érvényes: `===`, `in_array(..., true)`, `match` a `switch` helyett, `hash_equals()` a tokenekhez és `password_verify()` a jelszavakhoz.

## Egy összehasonlítás, amely igaz, noha nem kellene annak lennie

A PHP-alkalmazások sok biztonsági rése nem bonyolult függvényekben rejlik, hanem egyetlen karakterben: egy `==`-ben, ahová `===`-nek kellett volna kerülnie. A kód helyesnek látszik, a normál bemenetekkel végzett tesztek lefutnak, és az ellenőrzés mégis bizonyos feltételek mellett átenged valamit, amit meg kellene állítania.

Az ok a PHP egy tulajdonsága, amely a mindennapokban praktikus: az értékeket szükség esetén más típusra alakítja. Egy űrlapból származó karakterlánc így kerülőút nélkül összehasonlítható egy adatbázisból származó számmal. Bejelentkezéseknél, tokeneknél, szerepköröknél vagy állapotmezőknél éppen ez a nagyvonalúság válhat veszélyessé.

## Hogyan hasonlít a PHP a == operátorral

A PHP kézikönyve részletesen leírja a laza összehasonlítások szabályait. A biztonság szempontjából főleg ezek az esetek fontosak:

| Összehasonlítás | PHP 7 | PHP 8 | Ok |
|---|---|---|---|
| `0 == "a"` | igaz | hamis | a PHP 8.0 óta karakterláncként hasonlítja össze |
| `"1" == "01"` | igaz | igaz | mindkettő numerikus, összehasonlítás számként |
| `"10" == "1e1"` | igaz | igaz | az exponenciális írásmód numerikus |
| `100 == "1e2"` | igaz | igaz | szám numerikus karakterlánccal szemben |
| `null == false` | igaz | igaz | összehasonlítás logikai értékként |
| `"abc" == true` | igaz | igaz | a nem üres karakterlánc igaznak számít |

A PHP 8.0-ban bekövetkezett változás a hibák egy egész osztályát tompította: korábban egy nem numerikus karakterlánc egy számmal való összehasonlításnál `0`-vá vált. Ez a `switch` utasításokat is érintette. A táblázat többi sora viszont azt mutatja, hogy a laza összehasonlítások továbbra is tartogatnak meglepetéseket.

A `<`, `>` operátorok és a `<=>` összehasonlító operátor is ezeket a szabályokat követi. Rendezéseknél ez többnyire ártalmatlan, a „összeg kisebb a limitnél” típusú ellenőrzéseknél, a kérésből származó értékekkel, a típusnak előre rögzítettnek kell lennie.

Különösen alattomos az exponenciális írásmódú sor. A hash-értékek és a tokenek gyakran számjegyekből és betűkből állnak. Ha egy ilyen érték véletlenül úgy néz ki, mint egy exponenciális írásmódú szám, a `==` két különböző hasht esetleg egyformának hasonlít össze. Ez a háttere a régebbi PHP-alkalmazások bejelentkezés-megkerüléseiről szóló sok beszámolónak.

## PHP type juggling tipikus biztonsági ellenőrzésekben

A laza összehasonlítások több helyen rejtőznek, mint csak a `==` operátorban:

1. **Az `in_array()` és az `array_search()`** harmadik paraméter nélkül lazán hasonlít. A szerepkörök vagy azonosítók engedélyezési listája ezáltal lazább a gondoltnál.
2. **A `switch`** lazán hasonlít. A PHP 8.0 óta elérhető `match` kifejezés a kézikönyv szerint ezzel szemben típusbiztosan, `===`-vel hasonlít, és hibát dob, ha egyik ág sem illik.
3. **A JSON-bemenetek** valódi típusokat szolgáltatnak. Egy mező, amelyet karakterláncként vár, `true`-ként vagy számként is megérkezhet, és a `==` szerint meglepően egyezhet.
4. **A `strcmp()` titkokhoz** alkalmatlan, mert a futási idő a tartalomtól függ, és a hibaeseteket könnyen rosszul értékelik ki.
5. **A visszatérési értékek ellenőrzése**: az olyan függvények, mint a `strpos()`, `0`-t adnak az elején lévő találatra, és `false`-t a találat hiányára. Az `if (!strpos(...))` összekeveri a kettőt.

## Biztonságos összehasonlítások: a szembeállítás

A következő példák mindegyike a nem biztonságos és a biztonságos változatot mutatja:

```php
<?php
declare(strict_types=1);

// Szerepkör ellenőrzése
$erlaubteRollen = ['admin', 'redaktion'];
in_array($rolle, $erlaubteRollen);          // nem biztonságos: laza
in_array($rolle, $erlaubteRollen, true);    // biztonságos: szigorú

// Állapot kiértékelése
$aktion = match ($status) {                 // szigorú, ismeretlen értéknél hiba
    'offen'      => 'bearbeiten',
    'erledigt'   => 'archivieren',
};

// Token ellenőrzése a linkből
if ($tokenAusLink == $gespeichertesToken) { /* nem biztonságos */ }
if (is_string($tokenAusLink) && hash_equals($gespeichertesToken, $tokenAusLink)) { /* biztonságos */ }

// Jelszó ellenőrzése
if (md5($eingabe) == $hash) { /* nem biztonságos: gyenge eljárás és laza összehasonlítás */ }
if (password_verify($eingabe, $hash)) { /* biztonságos */ }

// Részstring keresése
if (strpos($text, 'abc') !== false) { /* korrekt: auch Treffer an Position 0 */ }
```

A `hash_equals()` a kézikönyv szerint a ismert titkot várja első, a felhasználó értékét második paraméterként, és állandó időben hasonlít. Hogyan hozhat létre egyáltalán biztonságosan tokeneket, azt a [Biztonságos véletlenszámok és tokenek PHP-ban](https://phpsos.de/hu/blog/sichere-tokens-php/) írja le.

> **Fontos:**
> A `declare(strict_types=1)` nem teszi szigorúvá az összehasonlításokat. Az utasítás csak a függvényhívásoknál és a visszatérési értékeknél történő típusátalakítást érinti. A laza összehasonlításokat Önnek kell lecserélnie a kódban.

A szigorú összehasonlításoknak van egy hátoldaluk: elbuknak, ha a típusok nem illenek össze. A `'42' === 42` hamis. Az űrlapokból és URL-ekből származó értékek a PHP-ban karakterláncként érkeznek, az adatbázisból származók a meghajtótól és a beállítástól függően karakterláncként vagy számként. Aki mindenhol `===`-t vezet be a típusok kiigazítása nélkül, olyan hibákat okoz, amelyek üzem közben jogos felhasználók elutasításaként tűnnek fel.

A megoldás egy világos határ: a bemeneteket fogadáskor validálja, és a megfelelő típusra hozza, például a `filter_var($wert, FILTER_VALIDATE_INT)` segítségével, majd a kódban már csak típusdeklarációkkal és szigorú összehasonlításokkal kezelje. A paraméterek, visszatérési értékek és tulajdonságok típusdeklarációi ebben segítenek, mert a hibás típusok így korán hibaüzenettel tűnnek fel, ahelyett hogy csendben eltűnnének egy összehasonlításban.

## A meglévő kód szisztematikus ellenőrzése

A gyarapodott projektekben a laza összehasonlítások százával fordulnak elő. Nem mindegyik probléma. Összpontosítson azokra a helyekre, ahol a hozzáférésről döntenek:

```bash
# Laza összehasonlítások biztonsági szempontból releváns kifejezéseket tartalmazó sorokban
grep -rnE "[^=!]==[^=]" src/ --include="*.php" | grep -iE "token|hash|pass|role|rolle|admin|auth|sign"
# in_array szigorú összehasonlítás nélkül
grep -rnE "in_array\([^)]*\)" src/ --include="*.php" | grep -v ", true)"
```

A statikus elemzés eszközei, mint a PHPStan vagy a Psalm, kérésre tartósan jelezhetik a laza összehasonlításokat. Így az új esetek egyáltalán nem kerülnek be a főágba. A típusok alapjait a [A PHP típusrendszere](https://phpsos.de/hu/php/typsystem/) írja le; hogyan hozza előre a megfelelő típusra a bemeneteket, azt a [Bemenetek validálása és szűrése PHP-ban](https://phpsos.de/hu/blog/eingaben-validieren-php/) mutatja.

## Mit jelent ez a régi rendszereknél

Azokat az alkalmazásokat, amelyek még PHP 7-en futnak, a régi, lazább összehasonlítási szabályok érintik. Ehhez jön egy második probléma: a PHP 8.1-re és régebbiekre a PHP-projekt nem ad több hivatalos biztonsági frissítést, a PHP2Shell ellen sem. A helyzetet a [PHP 8.1 és régebbiek: mi érvényes most](https://phpsos.de/hu/php2shell/php-8-1-und-aelter/) írja le.

A PHP 8-ra való migrációnál egyes összehasonlítási eredmények megváltoznak. Ez többnyire javulás, de elronthat olyan kódot, amely a régi viselkedésre támaszkodott. Ezért tervezzen olyan teszteket, amelyek célzottan lefedik a számokkal, üres értékekkel és `null`-lal végzett összehasonlításokat.

## Hogyan segít konkrétan a PHP SOS?

- Átvizsgáljuk a kódját laza összehasonlítások után azokon a helyeken, amelyek a bejelentkezésről, a jogokról vagy a fizetésekről döntenek.
- Lecseréljük őket szigorú összehasonlításokra, `match` kifejezésekre és megfelelő ellenőrző függvényekre.
- Úgy állítjuk be a statikus elemzést, hogy az új laza összehasonlítások feltűnjenek a buildben.
- A korszerű PHP-verziókra való migrációkat a megváltozott összehasonlítási szabályokra írt tesztekkel kísérjük.

A szoftver ellenőrzését a [biztonsági audit és penetrációs teszt](https://phpsos.de/hu/leistungen/sicherheitsaudit-penetrationstest/) írja le.


## Gyakori kérdések

**Mi a type juggling PHP-ban?**  
A PHP egyes műveleteknél automatikusan átalakítja a különböző típusú értékeket, például egy karakterláncot számmá. A == operátorral végzett laza összehasonlításnál ez az átalakítás dönti el az eredményt. Ez kényelmes, de olyan összehasonlításokhoz vezethet, amelyek igazak, noha az értékek nem egyformák.

**Megoldotta a PHP 8 a problémát?**  
Részben. A PHP 8.0 óta egy számot egy nem numerikus karakterlánccal karakterláncként hasonlít össze, így a 0 == 'a' már nem igaz. Két numerikus karakterláncot viszont továbbra is számként hasonlít össze, a true, false és null értékekkel végzett összehasonlítások pedig saját szabályokat követnek.

**Segít a declare(strict_types=1) a laza összehasonlítások ellen?**  
Nem. A strict_types azt szabályozza, hogy a skaláris típusokat átalakítják-e automatikusan a függvényhívásoknál és a visszatérési értékeknél. A == és != összehasonlító operátorokra nincs hatása.

**Hogyan hasonlítsam össze helyesen a jelszavakat és a tokeneket?**  
A jelszavakat a password_verify-vel ellenőrizze, a tokeneket és aláírásokat a hash_equals-szal. Mindkettő pontosan hasonlít, a hash_equals pedig állandó időben. A == vagy a strcmp ehhez alkalmatlan.

## Források

- [PHP kézikönyv: összehasonlító operátorok](https://www.php.net/manual/de/language.operators.comparison.php)
- [PHP kézikönyv: match](https://www.php.net/manual/de/control-structures.match.php)
- [OWASP Cheat Sheet Series](https://cheatsheetseries.owasp.org/)
