Na kratko: PHP za najpogostejše varnostne naloge prinaša že pripravljene, preverjene funkcije: password_hash() za gesla, random_bytes() in random_int() za žetone, htmlspecialchars() proti cross-site scriptingu, pripravljene poizvedbe s PDO proti SQL injection, hash_equals() za varne primerjave in razširitev Sodium za kriptografijo. Kdor te gradnike dosledno uporablja, se izogne večini vrzeli v aplikacijah. Proti napakam v samem interpreterju PHP, kot pri PHP2Shell, ne pomagajo; tam pomaga samo posodobitev.
Zakaj vgrajene varnostne funkcije PHP?
Pisanje varnostne kode samemu je nagnjeno k napakam. Lasten zgoščevalnik gesel, domač generator žetonov ali doma spletena sita za izpise HTML pogosto izgledajo pravilno, pa niso. Vgrajene funkcije so dokumentirane, preizkušene in se vzdržujejo s posodobitvami PHP.
Za odločevalce to pomeni: svoje agencije ali razvojne ekipe ne vprašajte, ali so bile varnostne funkcije napisane same, temveč ali se uporabljajo standardne funkcije jezika. Tabela daje pregled.
| Naloga | Funkcija | Na voljo od | Pomemben namig |
|---|---|---|---|
| Shranjevanje gesel | password_hash(), password_verify() |
PHP 5.5 | stolpec do 255 znakov; bcrypt skrajša na 72 bajtov |
| Naključni žetoni | random_bytes(), random_int() |
PHP 7.0 | nikoli rand() ali mt_rand() za skrivnosti |
| Naključnost objektno usmerjeno | Random\Randomizer |
PHP 8.2 | enaka varnost, bolj prilagodljiv vmesnik |
| Maskiranje izpisa HTML | htmlspecialchars() |
od nekdaj | privzete zastavice od 8.1 maskirajo tudi enojne narekovaje |
| Primerjava s konstantnim časom | hash_equals() |
PHP 5.6 | uporabnikov vnos kot drugi argument |
| Sodobna kriptografija | Sodium (sodium_*) |
PHP 7.2 | namesto lastnega šifriranja |
| Skrivnosti v sledeh klicev | #[\SensitiveParameter] |
PHP 8.2 | vrednost se v sledi ne pojavi |
| Preverjanje vnosov | filter_var() |
PHP 5.2 | preveri obliko, ne nadomesti maskiranja |
Gesla in žetoni
password_hash() ustvari soljen zgoščevalnik, password_verify() ga preveri. S PASSWORD_DEFAULT PHP trenutno uporablja bcrypt; od PHP 8.4 je privzeti faktor stroška po priročniku 12 namesto 10. Ker se lahko privzeti algoritem spremeni, priročnik priporoča stolpec podatkovne baze s 255 znaki. Pri bcrypt se gesla po 72 bajtih odrežejo, kar je treba upoštevati pri zelo dolgih geslih v obliki stavkov.
<?php
// Registracija
$hash = password_hash($passwort, PASSWORD_DEFAULT);
// Prijava, vključno s samodejno nadgradnjo starih zgoščenih vrednosti
if (password_verify($eingabe, $hash)) {
if (password_needs_rehash($hash, PASSWORD_DEFAULT)) {
$hash = password_hash($eingabe, PASSWORD_DEFAULT); // shraniti znova
}
}
// Žeton za povezavo za ponastavitev gesla
$token = bin2hex(random_bytes(32));
Za primerjave skrivnosti, na primer podpisa, je treba uporabiti hash_equals(). Običajna primerjava z === lahko po priročniku prek časa izvajanja izda, od katerega mesta se dve vrednosti razlikujeta. Podrobneje: Varno shranjevanje gesel v PHP in varna naključna števila in žetoni.
Maskiranje izpisov in varno poizvedovanje po podatkovni bazi
Cross-site scripting nastane, ko vnosi uporabnika nefiltrirani pristanejo v HTML. htmlspecialchars() posebne znake pretvori v entitete HTML. Od PHP 8.1 so privzete zastavice ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401; prej se enojni narekovaji niso pretvarjali. Kdor mora podpirati starejše različice, zastavice navede izrecno.
SQL injection preprečujejo pripravljene poizvedbe. Vrednosti se prenašajo ločeno od besedila SQL in poizvedbe ne morejo spremeniti:
<?php
$stmt = $pdo->prepare('SELECT id, name FROM kunden WHERE email = :email');
$stmt->execute(['email' => $email]);
$kunde = $stmt->fetch();
echo htmlspecialchars($kunde['name'] ?? '', ENT_QUOTES, 'UTF-8');
Podrobnosti so pod Preprečevanje cross-site scriptinga v PHP in na strani o SQL injection.
Preverjanje vnosov je drugi, ločen korak. filter_var() ugotovi, ali ima vrednost veljavno obliko, na primer e-poštni naslov ali celo število v dovoljenem območju. To pa ne nadomesti maskiranja pri izpisu in ne pripravljene poizvedbe, saj lahko tudi formalno veljavne vrednosti vsebujejo posebne znake.
Skrivnosti zadržati stran od sporočil o napakah
Premalo cenjeno uhajanje podatkov so sledi klicev (stack trace). Če v funkciji pride do napake, PHP zabeleži verigo klicev z argumenti. Če je tam geslo ali ključ API, pristane v dnevniku. Od PHP 8.2 atribut #[\SensitiveParameter] take parametre označi; njihova vrednost se v sledi nato pojavi samo kot objekt SensitiveParameterValue.
<?php
function anmelden(string $benutzer, #[\SensitiveParameter] string $passwort): bool
{
// ... preverjanje ...
throw new RuntimeException('Datenbank nicht erreichbar');
}
Po opombi v priročniku atribut deluje samo pri konkretni implementaciji, ne pri metodi vmesnika. Zato ga nastavite tam, kjer koda dejansko teče.
Seje: varne nastavitve in novi privzeti standardi
Seje so pogost cilj napadov. Tri nastavitve so osrednje: session.use_strict_mode=1 zavrne ID-je sej, ki jih strežnik ni sam ustvaril; session.cookie_httponly=1 prepreči dostop do piškotka seje prek JavaScripta; session.cookie_samesite=Lax otežuje sočasno pošiljanje pri tujih straneh. Po sprejetem RFC te tri vrednosti s PHP 8.6 postanejo privzete. Do takrat jih nastavite sami, in tudi potem izrecno, da konfiguracija ni odvisna od privzetih vrednosti.
session.use_strict_mode = 1
session.cookie_httponly = 1
session.cookie_samesite = Lax
session.cookie_secure = 1
Več o tem pod Zavarovanje sej v PHP.
Česa vgrajene funkcije ne zmorejo
Projekt PHP je v svoji varnostni politiki jasen: PHP ni peskovnik (sandbox). Zaobhodi open_basedir ali disable_functions, zlonamerni vnosi v unserialize() in neprevereni argumenti v funkcijah, kot je mysqli_query(), se ne štejejo za varnostne vrzeli PHP. Za upravljavce to pomeni: te nastavitve so koristno utrjevanje, ne pa varnostna meja, unserialize() pa s tujimi podatki nima kaj početi.
Druga točka zadeva PHP2Shell. CVE-2026-109618 in CVE-2026-109619 po navedbah php.net omogočata izvedbo kode, preden ali medtem ko vaša aplikacija teče; pri mod_php je bil exploit prikazan, še preden se sploh izvede koda aplikacije. Nobena funkcija v vaši kodi tega ne more ujeti. Varno programiranje in aktualna različica PHP sta dve ločeni obveznosti in izpolnjeni morata biti obe.
Kako PHP SOS pomaga konkretno?
V varnostnem pregledu za aplikacije PHP ciljno preverimo:
- Ravnanje z gesli in žetoni: ali se uporabljata
password_hash()inrandom_bytes(), ali obstajajo lastne izdelave? - Izpisi in poizvedbe: kje vnosi nefiltrirani pristanejo v HTML, SQL ali ukaze lupine?
- Konfiguracija sej: ali so Strict Mode, HttpOnly, SameSite in Secure nastavljeni in jih ni mogoče prepisati?
- Izvajalno okolje: ali teče podprta različica PHP s popravki z dne 21. 10. 2026?
Pogosta vprašanja
Kako se v PHP pravilno shranjujejo gesla?
S password_hash() in PASSWORD_DEFAULT, preverja se s password_verify(). Stolpec podatkovne baze naj po priročniku sprejme do 255 znakov, ker se lahko privzeti algoritem v prihodnje spremeni.
Ali je rand() primeren za žetone?
Ne. Za žetone, ključe in povezave za geslo je treba uporabiti random_bytes() ali random_int(). Po priročniku vračata kriptografsko varne naključne vrednosti.
Od kdaj je Sodium vključen v PHP?
Razširitev Sodium za sodobno kriptografijo je del PHP od PHP 7.2.0. Starejše različice so jo lahko dodale samo prek PECL.
Ali te funkcije ščitijo pred PHP2Shell?
Ne. Ščitijo pred napakami v vaši aplikaciji. PHP2Shell tiči v samem interpreterju in ga zapre samo posodobitev PHP, ki ji sledi ponovni zagon vseh procesov PHP.
Ali se privzete nastavitve sej v PHP 8.6 spreminjajo?
Da, po sprejetem RFC se session.use_strict_mode in session.cookie_httponly privzeto vklopita, session.cookie_samesite pa se nastavi na Lax. Obstoječe konfiguracije kljub temu nastavite sami in izrecno.