Ukratko: Pomoću expose_php = Off u php.ini nestaje zaglavlje X-Powered-By: PHP/…. Tome pripadaju server_tokens off; u Nginxu te ServerTokens Prod i ServerSignature Off u Apacheu. To nijedan poslužitelj ne čini sigurnijim, ali automatiziranim skeniranjima oduzima gotov popis verzija. Vlastito stanje zakrpa nakon toga provjeravate preko upravitelja paketa i procesa, a ne preko zaglavlja.
Posjetnica u svakom odgovoru
Tko je nakon 21. listopada 2026. htio znati koliko poslužitelja još prikazuje staru verziju PHP-a, nije morao dugo tražiti. Mnogi web-poslužitelji svoju verziju PHP-a navode u svakom pojedinom odgovoru, primjerice kao X-Powered-By: PHP/8.2.20. Internetske usluge pretraživanja te podatke čitaju usput, a Unit 42 tvrtke Palo Alto Networks za PHP2Shell je izbrojio više od 22,6 milijuna izloženih, potencijalno ranjivih instanci.
Za napadače je takav podatak prečac. Mogu unaprijed sortirati mete umjesto da nasumce isprobavaju. Za operatere je, nasuprot tome, beskoristan jer ne dokazuje ni vlastito stanje zakrpa: distribucije ispravke često isporučuju kao backport bez novog broja verzije.
Ovaj članak pokazuje gdje PHP i web-poslužitelj odaju podatke o verziji i kako ih isključiti. Otvoreno kaže i što to donosi, a što ne.
Gdje se sve pojavljuju podaci o verziji
Verzija PHP-a samo je jedno od više mjesta. Potpun pogled se isplati jer jedno zaboravljeno mjesto obezvrjeđuje ostale mjere.
| Mjesto nalaza | Primjer | Isključiti putem |
|---|---|---|
| HTTP zaglavlje PHP-a | X-Powered-By: PHP/8.3.x |
expose_php = Off u php.ini |
| HTTP zaglavlje web-poslužitelja | Server: nginx/1.x ili Server: Apache/2.4.x (Debian) |
server_tokens off; odnosno ServerTokens Prod |
| Stranice s pogreškama web-poslužitelja | redak s verzijom ispod stranica 404 ili 500 | iste postavke, u Apacheu dodatno ServerSignature Off |
| Zaglavlja aplikacije | vlastiti X-Powered-By podaci frameworka |
fastcgi_hide_header odnosno Header unset |
| Datoteke u web-korijenu | testne datoteke phpinfo(), readme i changelog datoteke |
izbrisati ili blokirati |
| PHP poruke o pogreškama | putovi i pojedinosti o verziji u pregledniku | display_errors = Off |
Testne datoteke s phpinfo() pritom su najizdašniji izvor za napadače. Otkrivaju ne samo verziju nego cijelu konfiguraciju; više o tome u članku phpinfo() datoteke: zašto moraju nestati.
Isključiti expose_php u php.ini
Prema PHP priručniku expose_php je zadano uključen i može se mijenjati samo u php.ini, ne u poolu, ne putem .htaccess i ne tijekom rada. Vrijednost stoga postavite u php.ini svakog načina rada koji isporučuje web-stranice, dakle kod Debiana i Ubuntua primjerice pod /etc/php/<version>/fpm/ i po potrebi /etc/php/<version>/apache2/.
; php.ini web SAPI-ja, zatim ponovno pokrenuti PHP-FPM odnosno Apache
expose_php = Off
Ako paralelno radi više verzija PHP-a, svaka treba vlastiti unos. Ponovno pokretanje dotične usluge je obvezno jer PHP php.ini čita samo pri pokretanju.
Skratiti Server zaglavlja u Nginxu i Apacheu
Nginx svoju verziju zadano navodi u zaglavlju Server i na stranicama s pogreškama. Pomoću server_tokens off; ostaje samo naziv proizvoda. Uz to se zaglavlje X-Powered-By koje aplikacija sama postavlja može izfiltrirati pomoću fastcgi_hide_header. Prema dokumentaciji Nginxa on od sebe skriva samo Status i X-Accel-…, a ne X-Powered-By.
# u http bloku, vrijedi za sve web-stranice
server_tokens off;
fastcgi_hide_header X-Powered-By;
U Apacheu ServerTokens upravlja zaglavljem Server i istodobno retkom s verzijom ispod stranica s pogreškama. Postavka prema dokumentaciji Apachea vrijedi samo na razini cijelog poslužitelja, a ne po VirtualHostu. Budući da odgovori PHP-FPM-a putem mod_proxy_fcgi završavaju u drugoj tablici zaglavlja od odgovora samog poslužitelja, uklanjanje postavite u obje varijante.
# konfiguracija na razini poslužitelja, npr. /etc/apache2/conf-available/security.conf
ServerTokens Prod
ServerSignature Off
# zahtijeva mod_headers
<IfModule mod_headers.c>
Header unset X-Powered-By
Header always unset X-Powered-By
</IfModule>
Prije ponovnog učitavanja provjerite konfiguraciju naredbom nginx -t odnosno apachectl configtest. Kako zaglavljima aktivno uključiti zaštitu za posjetitelje, primjerice HSTS-om ili Content-Security-Policyjem, opisuje vodič Sigurnosna zaglavlja za web-stranice.
Kako izvana provjeriti rezultat
Nakon prelaska provjerite svaku domenu zasebno, po mogućnosti i jednu stranicu s pogreškom. Sljedeće naredbe šalju samo uobičajene zahtjeve na Vaše vlastite web-stranice:
# Zaglavlja početne stranice
curl -sI https://www.example.de/ | grep -iE '^(server|x-powered-by):'
# Zaglavlja nepostojeće stranice
curl -sI https://www.example.de/gibt-es-nicht-4711 | grep -iE '^(server|x-powered-by):'
Očekuje se zaglavlje Server bez broja verzije i bez X-Powered-By. Ako tamo i dalje stoji verzija PHP-a, najčešće je aktivna druga php.ini ili usluga još nije ponovno pokrenuta. Ako se ispred poslužitelja nalazi CDN, dodatno izravno provjerite izvorni poslužitelj, ako je dostupan.
Verziju PHP-a ipak interno zabilježite: u inventaru poslužitelja, sa stanjem paketa i datumom zadnje provjere. Stanje zakrpa dokazujete preko poslužitelja, kako je opisano u članku Pravilno provjeriti verziju PHP-a i stanje zakrpa.
Što skrivanje donosi, a što ne
Pošteno svrstano, isključivanje podataka o verziji mala je mjera s malim troškom. Smanjuje vjerojatnost da se pojavite u ciljanim pretragama određenih verzija i smanjuje informacije koje napadač dobiva bez truda.
Ne štiti, međutim, od automatiziranih napada koji svoje zahtjeve jednostavno šalju svakom dostupnom poslužitelju. Kako takva masovna skeniranja teku, opisuje članak Masovna skeniranja nezakrpanog PHP-a. Protiv PHP2Shella pomaže samo ažuriranje na PHP 8.5.12, 8.4.27, 8.3.36 ili 8.2.35 odnosno potvrđeni backport, nakon čega slijedi ponovno pokretanje svih PHP procesa.
Nuspojava je, nasuprot tome, izrazito korisna: tko isključi podatke o verziji prema van, mora svoje verzije interno uredno bilježiti. U situaciji poput PHP2Shella upravo taj pregled odlučuje o tome koliko brzo znate koji su poslužitelji pogođeni.
Skrivanje zato ima smisla kao dio osnovnog ojačavanja, a ne kao pojedinačna mjera. Pripada u isti kontrolni popis kao isključeni ispisi pogrešaka, izbrisane testne datoteke i zaključane konfiguracijske vrijednosti.
Kako PHP SOS konkretno pomaže?
- Zabilježiti mjesta nalaza: zaglavlja, stranice s pogreškama i web-korijen svih domena provjeravamo na podatke o verziji i zaboravljene testne datoteke.
- Prilagoditi konfiguraciju:
expose_php, Server zaglavlja i filtre zaglavlja postavljamo u svakoj verziji PHP-a i svakom web-poslužitelju te kontrolirano ponovno pokrećemo usluge. - Stanje zakrpa dokazati interno: verzije i stanja paketa dokumentiramo tamo gdje pripadaju, u inventaru umjesto u HTTP zaglavlju.
- Ugraditi u ojačavanje: mjera je dio našeg ojačavanja poslužitelja za PHP web-poslužitelje.
Česta pitanja
Čini li expose_php = Off moj poslužitelj sigurnim?
Ne. Postavka uklanja samo podatak o verziji iz HTTP zaglavlja. Ranjiva verzija PHP-a ostaje ranjiva, a automatizirani napadi svoje zahtjeve ionako često isprobavaju na svakom dostupnom poslužitelju. Ali napadačima oduzima jednostavan prethodni odabir i smanjuje vidljivost u tražilicama za poslužitelje.
Mogu li expose_php postaviti u FPM poolu ili putem .htaccess?
Prema PHP priručniku expose_php može se mijenjati samo u php.ini. Zato vrijednost postavite u php.ini svakog web SAPI-ja, a zatim ponovno pokrenite PHP-FPM ili Apache.
Zašto vidim X-Powered-By unatoč expose_php = Off?
Tada zaglavlje najčešće sama postavlja aplikacija ili framework, ili je aktivna druga php.ini. Zaglavlje dodatno uklonite u web-poslužitelju: pomoću fastcgi_hide_header u Nginxu ili Header unset u Apacheu.
Kako provjeriti stanje zakrpa ako verzija više nije vidljiva?
Preko samog poslužitelja: upravitelj paketa, changelog paketa s CVE brojevima i vrijeme pokretanja PHP procesa. Podatak u HTTP zaglavlju ionako nije bio dokaz jer distribucije backportove često ugrađuju bez novog broja verzije.