Īsi: PHP unserialize drošībā ir vienkāršs noteikums: datus, kas nāk no ārpuses, nekad neapstrādāt ar unserialize(). Tā vietā izmantojiet JSON. Kur unserialize() no vecā koda jāatstāj, ierobežojiet to ar allowed_classes => false un datu īstumu pārbaudiet ar parakstu.
Kas ar unserialize() var noiet greizi
PHP ar serialize() var masīvus un objektus pārvērst rakstzīmju virknē un ar unserialize() tos atjaunot. Tas ir ērti, lai datus ievietotu sīkdatnēs, kešatmiņās vai datubāzes laukos. Problēma: unserialize() tā rezultātā veido arī jebkuru klašu objektus, kuras lietojumprogrammā var ielādēt.
Veidojot un vēlākais iznīcinot šādu objektu, PHP automātiski izsauc noteiktas metodes, piemēram, __wakeup(), __unserialize() vai __destruct(). Ja klasēs projektā vai bibliotēkās ir šādas metodes ar failu, datubāzes vai sistēmas darbībām, uzbrucējs tās ar manipulētiem datiem var izmantot citam mērķim. Speciālisti runā par PHP Object Injection. Atkarībā no esošajām klasēm ietekme sniedzas no atsevišķu failu dzēšanas līdz ļaunprātīga koda izpildei.
Netehniski apkopojot: viens pārdomāts izsaukums nepareizā vietā var ielaist uzbrucēju dziļi lietojumprogrammā. Šī ievainojamība tāpēc jau gadiem pieder pie OWASP Top 10 tēmām.
Drošā alternatīva: JSON nevis unserialize
Gandrīz visos gadījumos, kad dati nāk no ārpuses vai atstāj serveri, pareizā izvēle ir JSON. json_decode() veido tikai masīvus, skaitļus, rakstzīmju virknes un patiesuma vērtības, nevis paša klašu objektus.
<?php
declare(strict_types=1);
function leseEinstellungen(string $eingabe): array
{
try {
$daten = json_decode($eingabe, true, 16, JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
throw new InvalidArgumentException('Ungültige Daten.');
}
if (!is_array($daten)) {
throw new InvalidArgumentException('Erwartet wurde ein Objekt.');
}
// Pārņemt tikai zināmus laukus ar gaidītajiem tipiem
return [
'sprache' => in_array($daten['sprache'] ?? '', ['de', 'en'], true) ? $daten['sprache'] : 'de',
'seiten' => max(1, min(100, (int) ($daten['seiten'] ?? 20))),
];
}
Svarīga ir otrā daļa: arī JSON dati ir ievade. Pārņemiet tikai gaidītos laukus un pārbaudiet tipus un vērtību diapazonus, pirms ar tiem strādājat.
Ja unserialize nav izvairāms: allowed_classes un paraksts
Vecākās lietojumprogrammās serializēti dati bieži atrodas datubāzēs vai kešatmiņās, ko nevar pārslēgt uzreiz. Tad risku ierobežojat divos soļos. Pirmkārt, ar allowed_classes novēršat objektu veidošanu. Otrkārt, ar HMAC parakstu pārliecināties, ka dati patiešām nāk no jūsu lietojumprogrammas un ceļā nav mainīti.
<?php
declare(strict_types=1);
const SCHLUESSEL = 'aus-sicherer-konfiguration-laden'; // nekad koda repozitorijā
function verpacken(array $daten): string
{
$roh = serialize($daten);
return hash_hmac('sha256', $roh, SCHLUESSEL) . '.' . base64_encode($roh);
}
function auspacken(string $paket): array
{
[$signatur, $b64] = array_pad(explode('.', $paket, 2), 2, '');
$roh = base64_decode($b64, true);
if ($roh === false || !hash_equals(hash_hmac('sha256', $roh, SCHLUESSEL), $signatur)) {
throw new RuntimeException('Signatur ungültig.');
}
$daten = unserialize($roh, ['allowed_classes' => false, 'max_depth' => 16]);
if (!is_array($daten)) {
throw new RuntimeException('Unerwartetes Format.');
}
return $daten;
}
Ja tomēr vajadzīgas noteiktas klases, nosauciet tās atsevišķi allowed_classes, nevis atļaujiet visas. Atslēgai jāatrodas servera konfigurācijā vai noslēpumu glabātuvē, un pēc incidenta tā jāatjauno.
Esošo datu pakāpeniska pārslēgšana uz JSON
Izaugušās lietojumprogrammās serializētas vērtības datubāzē bieži atrodas gadiem. Pārslēgšana vienā solī tad ir riskanta. Ir noderējusi pārejas shēma: lietojumprogramma lasa abus formātus, bet raksta tikai JSON. Nolasot vecu vērtību, tā vienreiz tiek izpakota ar allowed_classes => false un uzreiz atpakaļ ierakstīta kā JSON. Pēc kāda laika lielākā daļa ierakstu ir pārslēgti, un vienreizējs skripts nokārto pārējo.
Pārejas laikā protokolējiet, cik bieži vēl tiek lasīts vecais formāts, kā arī gadījumus, kad paraksti nesakrīt. Šādas neveiksmes var norādīt uz manipulāciju mēģinājumiem, un tām jābūt uzraudzībā. Tikai kad vecais formāts vairs neparādās, unserialize() atzaru no koda noņemiet pilnībā.
Slēpta deserializācija: sesijas, kešatmiņas un failu ceļi
Ne katra deserializācija ir redzama paša kodā. Pārbaudiet arī šīs vietas:
- Sesijas: PHP sesiju datus serializē automātiski. Kamēr uzbrucēji nevar rakstīt sesiju failus, tas nav kritiski. Atsevišķus sesiju ceļus katrai vietnei apraksta ceļvedis Sesiju nodrošināšana PHP.
- Sesiju serializētājs: Norāde par CVE-2026-115560 skar neparastu konfigurāciju, kurā
serialize_handlerizpildes laikā tiek pārslēgts arini_set()un ir aktīvs sesijas augšupielādes progress. Atvieglojums saskaņā ar php.net:session.upload_progress.enabled=0failā php.ini vai FPM pūlā, pēc tam darbprocesu pārstartēšana. - Kešatmiņas un rindas: Redis, Memcached vai datubāzes tabulas ar serializētām vērtībām ir tik drošas, cik droša ir piekļuve tām. Tām nav jābūt atklāti tīklā.
- Failu funkcijas ar ceļiem no ievades: Failu funkcijām nekad nenododiet ceļus no pieprasījuma. Stream wrapper, piemēram,
phar://, citādi var izraisīt negaidītu apstrādi. Atļauju saraksts faila nosaukumiem šeit aizsargā uzticamāk nekā jebkāda filtrēšana. - Atkarības: Bibliotēkas ar zināmām ievainojamībām paplašina uzbrucēja iespējas. Uzturiet tās aktuālas, kā aprakstīts ceļvedī Composer atkarību pārbaude uz ievainojamībām.
Riskantu vietu atrašana kodā
Pirmo inventarizāciju var veikt ar vienkāršu meklēšanu. Tā sistēmā neko nemaina un parāda visus izsaukumus ārpus vendor direktorija:
grep -rn --include='*.php' 'unserialize(' /var/www/projekt \
| grep -v '/vendor/'
Vērtējiet katru atradumu ar trim jautājumiem: no kurienes nāk dati? Vai lietotājs tos var mainīt? Vai allowed_classes ir iestatīts? Atradumi ar ievadi no sīkdatnēm, $_GET, $_POST, $_REQUEST vai ārējām saskarnēm ir prioritāri. Aizstājiet tos ar JSON un pārslēgšanu pārbaudiet, pirms to izvēršat.
Vēl viena norāde atrodas pārlūkā. Serializētiem PHP datiem ir tipiska uzbūve ar tipu apzīmējumiem un garuma norādēm. Ja šādas vērtības atrodat sīkdatnēs, slēptos veidlapu laukos vai savas vietnes parametros, lietojumprogramma serializētus datus ir nodevusi apmeklētājam un, visticamāk, gaida tos atpakaļ. Tieši šīs vietas jāpārbauda.
Vadībai pietiek ar vienkāršu jautājumu izstrādātājiem vai aģentūrai: „Vai mūsu lietojumprogramma kaut kur apstrādā serializētus datus, kas nāk no ārpuses, un ja jā, kā tie ir aizsargāti?“ Skaidra atbilde ar atrašanās vietām ir laba zīme. Izvairīga atbilde ir iemesls lietojumprogrammu ļaut pārbaudīt.
Kā PHP SOS palīdz konkrēti?
- Pārmeklējam jūsu kodu uz deserializāciju ar svešiem datiem un vērtējam katru atradumu pēc sasniedzamības un ietekmes.
- Piedāvājam konkrētas pārslēgšanas uz JSON vai nodrošinātiem izsaukumiem un atbalstām jūsu izstrādātājus īstenošanā.
- Pārbaudām atkarības uz zināmām ievainojamībām un dokumentējam stāvokli.
- Pēc vēlēšanās lietojumprogrammu ļaujam neatkarīgi pārbaudīt drošības audita un penetrācijas testa ietvaros.
Biežāk uzdotie jautājumi
Vai unserialize() PHP principā ir nedrošs?
Ne datiem, ko ģenerē tikai jūsu pašu lietojumprogramma un ko neviens nevar mainīt. Bīstami kļūst, tiklīdz lietotāji vai trešās personas var ietekmēt ievadi, piemēram, caur sīkdatnēm, veidlapu laukiem vai ārējām saskarnēm.
Vai allowed_classes => false kā aizsardzība pietiek?
Tas neļauj izveidot patvaļīgus objektus un tādējādi aizver svarīgāko uzbrukuma ceļu. Ārējai ievadei JSON tomēr ir labāka izvēle, jo parsētājs ir vienkāršāks un objektus neatjauno.
Vai bibliotēku atjaunināšana palīdz pret deserializācijas uzbrukumiem?
Daļēji. Uzbrukumi bieži izmanto atkarību klases, kas objekta iznīcināšanas vai pamodināšanas brīdī izpilda darbības. Aktuālas pakotnes aizver zināmās ķēdes, taču īstais cēlonis ir unserialize izsaukums ar svešiem datiem.
Vai PHP2Shell ir kāds sakars ar deserializāciju?
Abas galvenās ievainojamības ir PHP pieprasījumu apstrādē. Norāde par CVE-2026-115560 turpretī skar neparastu sesiju serializācijas konfigurāciju. Atvieglojums tur ir session.upload_progress.enabled=0.