Aktuālā situācija: PHP2Shell. Divas kritiskas PHP ievainojamības (CVSS 9.2) ļauj izpildīt ļaunprātīgu kodu bez pieteikšanās. Ielāpi kopš 21.10.2026.Kas jādara tagad →

Type Juggling PHP: brīvie salīdzinājumi kā drošības risks

Kāpēc brīvie salīdzinājumi ar == PHP var radīt drošības nepilnības, ko mainīja PHP 8.0 un kā salīdzinājumus pieteikšanās, tokenu un atļauju pārbaudēs uzrakstīt noturīgi.

Aktuāls uz 5 min lasīšanas laiksDroša izstrāde

Īsi sakot: PHP Type Juggling apzīmē automātisko tipu pārveidi, kas brīvā salīdzinājumā ar == nosaka rezultātu. Kopš PHP 8.0 noteikumi ir stingrāki, taču numeriskas virknes joprojām salīdzina kā skaitļus, un true, false un null ir savi noteikumi. Drošībai svarīgām pārbaudēm tāpēc ir spēkā: ===, in_array(..., true), match switch vietā, hash_equals() tokeniem un password_verify() parolēm.

Salīdzinājums, kas ir patiess, lai gan nedrīkstētu būt

Daudzas drošības nepilnības PHP lietojumprogrammās slēpjas nevis sarežģītās funkcijās, bet vienā rakstzīmē: == vietā, kur vajadzēja ===. Kods izskatās pareizs, testi ar parastu ievadi iziet, un tomēr noteiktos apstākļos pārbaude ļauj notikt tam, ko tai vajadzēja apturēt.

Cēlonis ir PHP īpašība, kas ikdienā ir praktiska: vērtības pēc vajadzības tiek pārveidotas citā tipā. Virkni no veidlapas tā var bez līkločiem salīdzināt ar skaitli no datubāzes. Pieteikšanās, tokenu, lomu vai statusa lauku gadījumā tieši šī devība var kļūt bīstama.

Kā PHP salīdzina ar ==

PHP rokasgrāmata brīvo salīdzinājumu noteikumus apraksta detalizēti. Drošībai svarīgi ir galvenokārt šie gadījumi:

Salīdzinājums PHP 7 PHP 8 Iemesls
0 == "a" patiess nepatiess kopš PHP 8.0 salīdzina kā virknes
"1" == "01" patiess patiess abas numeriskas, salīdzinājums kā skaitļi
"10" == "1e1" patiess patiess eksponenciālais pieraksts ir numerisks
100 == "1e2" patiess patiess skaitlis pret numerisku virkni
null == false patiess patiess salīdzinājums kā patiesuma vērtība
"abc" == true patiess patiess netukša virkne skaitās patiesa

Izmaiņa PHP 8.0 ir mīkstinājusi veselu kļūdu klasi: agrāk nenumeriska virkne salīdzinājumā ar skaitli kļuva par 0. Tas skāra arī switch izteikumus. Tabulas pārējās rindas tomēr rāda, ka brīvie salīdzinājumi joprojām slēpj pārsteigumus.

Šiem noteikumiem seko arī operatori <, > un salīdzināšanas operators <=>. Šķirošanā tas parasti ir nekaitīgi, bet pārbaudēs, piemēram, „summa mazāka par limitu" ar vērtībām no pieprasījuma tips jānosaka iepriekš.

Īpaši mānīga ir rinda ar eksponenciālo pierakstu. Jaucējkodu vērtības un tokeni bieži sastāv no cipariem un burtiem. Ja šāda vērtība nejauši izskatās kā skaitlis eksponenciālā pierakstā, == divus dažādus jaucējkodus var uzskatīt par vienādiem. Tas ir fons daudziem ziņojumiem par pieteikšanās apiešanu vecākās PHP lietojumprogrammās.

PHP Type Juggling tipiskās drošības pārbaudēs

Brīvie salīdzinājumi slēpjas vairākās vietās, ne tikai operatorā ==:

  1. in_array() un array_search() bez trešā parametra salīdzina brīvi. Lomu vai ID atļauju saraksts tādējādi kļūst mīkstāks, nekā domāts.
  2. switch salīdzina brīvi. Kopš PHP 8.0 pieejamā izteiksme match saskaņā ar rokasgrāmatu turpretī salīdzina tipdrošī ar === un met kļūdu, ja neder neviens zars.
  3. JSON ievade dod īstus tipus. Lauks, ko gaidāt kā virkni, var ierasties kā true vai kā skaitlis un ar == negaidīti sakrist.
  4. strcmp() noslēpumiem nav piemērots, jo izpildes laiks atkarīgs no satura un kļūdu gadījumi viegli tiek nepareizi izvērtēti.
  5. Atgriežamo vērtību pārbaude: tādas funkcijas kā strpos() atdod 0 par trāpījumu sākumā un false par trāpījuma neesamību. if (!strpos(...)) abus sajauc.

Droši salīdzinājumi: pretstatījums

Turpmākie piemēri katrā gadījumā rāda nedrošo un drošo variantu:

<?php
declare(strict_types=1);

// Pārbaudīt lomu
$erlaubteRollen = ['admin', 'redaktion'];
in_array($rolle, $erlaubteRollen);          // nedroši: brīvi
in_array($rolle, $erlaubteRollen, true);    // droši: stingri

// Izvērtēt statusu
$aktion = match ($status) {                 // stingri, kļūda nezināmai vērtībai
    'offen'      => 'bearbeiten',
    'erledigt'   => 'archivieren',
};

// Pārbaudīt tokenu no saites
if ($tokenAusLink == $gespeichertesToken) { /* nedroši */ }
if (is_string($tokenAusLink) && hash_equals($gespeichertesToken, $tokenAusLink)) { /* droši */ }

// Pārbaudīt paroli
if (md5($eingabe) == $hash) { /* nedroši: vāja metode un brīva salīdzināšana */ }
if (password_verify($eingabe, $hash)) { /* droši */ }

// Meklēt apakšvirkni
if (strpos($text, 'abc') !== false) { /* korrekt: auch Treffer an Position 0 */ }

hash_equals() saskaņā ar rokasgrāmatu kā pirmo parametru gaida zināmo noslēpumu un kā otro lietotāja vērtību un salīdzina nemainīgā laikā. Kā tokenus vispār droši izveidot, apraksta Droši gadījuma skaitļi un tokeni PHP.

Svarīgi

declare(strict_types=1) salīdzinājumus nepadara stingrus. Šī instrukcija attiecas tikai uz tipu pārveidi funkciju izsaukumos un atgriežamajās vērtībās. Brīvie salīdzinājumi kodā jāaizstāj pašiem.

Stingriem salīdzinājumiem ir otra puse: tie neizdodas, ja tipi nesakrīt. '42' === 42 ir nepatiess. Vērtības no veidlapām un URL PHP ierodas kā virknes, vērtības no datubāzes atkarībā no draivera un iestatījuma kā virknes vai skaitļi. Kas visur ievieš ===, neizlīdzinot tipus, tas rada kļūdas, kas ekspluatācijā izpaužas kā pamatotu lietotāju noraidīšana.

Risinājums ir skaidra robeža: ievade tiek validēta saņemšanas brīdī un pārvērsta pareizajā tipā, piemēram, ar filter_var($wert, FILTER_VALIDATE_INT), un pēc tam kodā apstrādāta tikai ar tipu deklarācijām un stingriem salīdzinājumiem. Tipu deklarācijas parametriem, atgriezumiem un īpašībām palīdz tādēļ, ka nepareizi tipi tad agri izpaužas ar kļūdas ziņojumu, nevis kluso pazūd salīdzinājumā.

Esošā koda sistemātiska pārbaude

Izaugušos projektos brīvo salīdzinājumu ir simtiem. Ne katrs ir problēma. Koncentrējieties uz vietām, kur tiek pieņemts lēmums par piekļuvi:

# Brīvie salīdzinājumi rindās ar drošībai svarīgiem jēdzieniem
grep -rnE "[^=!]==[^=]" src/ --include="*.php" | grep -iE "token|hash|pass|role|rolle|admin|auth|sign"
# in_array bez stingrā salīdzinājuma
grep -rnE "in_array\([^)]*\)" src/ --include="*.php" | grep -v ", true)"

Statiskās analīzes rīki, piemēram, PHPStan vai Psalm, pēc vēlēšanās var brīvos salīdzinājumus ziņot pastāvīgi. Tā jauni gadījumi vispār nenonāk galvenajā zarā. Pamatus par tipiem apraksta PHP tipu sistēma; kā ievadi iepriekš pārvērst pareizajā tipā, rāda Ievades validēšana un filtrēšana PHP.

Ko tas nozīmē vecām sistēmām

Lietojumprogrammas, kas vēl darbojas ar PHP 7, skar vecie, brīvākie salīdzināšanas noteikumi. Klāt nāk otra problēma: PHP 8.1 un vecākām versijām PHP projekts oficiālus drošības atjauninājumus vairs nesniedz, arī ne pret PHP2Shell. Situāciju apraksta PHP 8.1 un vecākas: kas tagad spēkā.

Migrējot uz PHP 8, daži salīdzinājumu rezultāti mainās. Tas parasti ir uzlabojums, bet var salauzt kodu, kas paļāvies uz veco uzvedību. Tāpēc plānojiet testus, kas mērķtiecīgi aptver salīdzinājumus ar skaitļiem, tukšām vērtībām un null.

Kā PHP SOS palīdz konkrēti?

  • Mēs pārmeklējam jūsu kodu uz brīvajiem salīdzinājumiem vietās, kas lemj par pieteikšanos, tiesībām vai maksājumiem.
  • Mēs tos aizstājam ar stingriem salīdzinājumiem, match izteiksmēm un piemērotām pārbaudes funkcijām.
  • Mēs iestatām statisko analīzi tā, lai jauni brīvie salīdzinājumi izpaustos būvējumā.
  • Mēs pavadām migrācijas uz aktuālajām PHP versijām ar testiem mainītajiem salīdzināšanas noteikumiem.

Jūsu programmatūras pārbaudi apraksta drošības audits un penetrācijas tests.

Biežāk uzdotie jautājumi

Kas ir Type Juggling PHP?

PHP dažās darbībās dažādu tipu vērtības pārveido automātiski, piemēram, virkni par skaitli. Brīvā salīdzinājumā ar == šī pārveide nosaka rezultātu. Tas ir ērti, bet var novest pie salīdzinājumiem, kas ir patiesi, kaut gan vērtības neizskatās vienādas.

Vai PHP 8 problēmu atrisināja?

Daļēji. Kopš PHP 8.0 skaitli ar nenumerisku virkni salīdzina kā virknes, tātad 0 == 'a' vairs nav patiess. Tomēr divas numeriskas virknes joprojām salīdzina kā skaitļus, un salīdzinājumiem ar true, false un null ir savi noteikumi.

Vai declare(strict_types=1) palīdz pret brīvajiem salīdzinājumiem?

Nē. strict_types nosaka, vai skalārie tipi funkciju izsaukumos un atgriezumos tiek pārveidoti automātiski. Uz salīdzināšanas operatoriem == un != tam ietekmes nav.

Kā pareizi salīdzināt paroles un tokenus?

Paroles pārbaudiet ar password_verify, tokenus un parakstus ar hash_equals. Abas salīdzina precīzi, un hash_equals nemainīgā laikā. Salīdzinājums ar == vai strcmp tam nav piemērots.

Avoti

Tūlītējs kontakts

Katra stunda ir svarīga. Rakstiet mums tūlīt.

Īsi aprakstiet sistēmu, PHP versiju un novēroto. Mēs sazināsimies, noskaidrosim situāciju un strādāsim attālināti, līdz incidents ir slēgts un dokumentēts.

Tūlītēja palīdzība