Накратко: С expose_php = Off в php.ini заглавието X-Powered-By: PHP/… изчезва. Към това се добавят server_tokens off; в Nginx, както и ServerTokens Prod и ServerSignature Off в Apache. Това не прави сървъра по-сигурен, но отнема на автоматизираните сканирания готов списък с версии. Собственото състояние на пачовете проверявате след това чрез управлението на пакети и процесите, не чрез заглавия.
Визитката във всеки отговор
Който след 21 октомври 2026 г. е искал да знае колко сървъра още показват стара версия на PHP, не е трябвало да търси дълго. Много уеб сървъри посочват версията на PHP във всеки отделен отговор, например като X-Powered-By: PHP/8.2.20. Услуги за търсене в целия интернет четат тези данни, а Unit 42 на Palo Alto Networks преброи за PHP2Shell повече от 22,6 милиона изложени, потенциално уязвими инстанции.
За атакуващите такава информация е пряк път. Те могат да подреждат целите предварително, вместо да пробват на сляпо. За операторите тя е безполезна, защото не доказва дори собственото състояние на пачовете: дистрибуциите често доставят корекциите като backport без нов номер на версията.
Тази статия показва къде PHP и уеб сървърите издават информация за версията и как да спрете данните. Тя казва и открито какво носи това и какво не.
Къде навсякъде се появяват данни за версията
Версията на PHP е само едно от няколко места. Пълният поглед си струва, защото едно-единствено забравено място обезценява другите мерки.
| Място на находката | Пример | Спиране чрез |
|---|---|---|
| HTTP заглавие от PHP | X-Powered-By: PHP/8.3.x |
expose_php = Off в php.ini |
| HTTP заглавие на уеб сървъра | Server: nginx/1.x или Server: Apache/2.4.x (Debian) |
server_tokens off; респ. ServerTokens Prod |
| Страници с грешки на уеб сървъра | ред с версия под страниците 404 или 500 | същите настройки, в Apache допълнително ServerSignature Off |
| Заглавия на приложението | собствени X-Powered-By на рамки |
fastcgi_hide_header респ. Header unset |
| Файлове в уеб корена | тестови файлове с phpinfo(), файлове readme и changelog |
изтриване или блокиране |
| Съобщения за грешки на PHP | пътища и подробности за версията в браузъра | display_errors = Off |
Тестовите файлове с phpinfo() са най-богатият източник за атакуващите. Те издават не само версията, а цялата конфигурация; повече по темата в статията Файлове с phpinfo(): защо трябва да изчезнат.
Изключване на expose_php в php.ini
Според ръководството на PHP expose_php е включен по подразбиране и може да се променя само в php.ini, не в пула, не чрез .htaccess и не по време на изпълнение. Затова задайте стойността в php.ini на всеки режим на работа, който връща сайтове, тоест при Debian и Ubuntu например в /etc/php/<version>/fpm/ и при нужда в /etc/php/<version>/apache2/.
; php.ini на уеб SAPI, след това рестартирайте PHP-FPM респ. Apache
expose_php = Off
Ако успоредно работят няколко версии на PHP, всяка се нуждае от своя запис. Рестартирането на съответната услуга е задължително, защото PHP чете php.ini само при стартиране.
Съкращаване на Server заглавията в Nginx и Apache
Nginx по подразбиране посочва версията си в заглавието Server и на страниците с грешки. С server_tokens off; остава само името на продукта. Допълнително заглавие X-Powered-By, което приложението задава само, може да се филтрира с fastcgi_hide_header. Според документацията на Nginx той скрива от само себе си само Status и X-Accel-…, не и X-Powered-By.
# в блока http, важи за всички сайтове
server_tokens off;
fastcgi_hide_header X-Powered-By;
В Apache ServerTokens управлява заглавието Server и едновременно реда с версията под страниците с грешки. Според документацията на Apache настройката важи само за целия сървър, не за всеки VirtualHost. Тъй като отговорите на PHP-FPM през mod_proxy_fcgi попадат в друга таблица със заглавия, различна от тази на отговорите на самия сървър, задайте премахването и в двата варианта.
# конфигурация за целия сървър, напр. /etc/apache2/conf-available/security.conf
ServerTokens Prod
ServerSignature Off
# изисква mod_headers
<IfModule mod_headers.c>
Header unset X-Powered-By
Header always unset X-Powered-By
</IfModule>
Преди презареждане проверете конфигурацията с nginx -t респективно apachectl configtest. Как да включите активна защита на посетителите чрез заглавия, например с HSTS или Content-Security-Policy, описва ръководството Хедъри за сигурност на уебсайтове.
Така проверявате резултата отвън
След прехода проверете всеки домейн поотделно, по възможност и страница с грешка. Следните команди изпращат само нормални заявки към Вашите собствени сайтове:
# Заглавия на началната страница
curl -sI https://www.example.de/ | grep -iE '^(server|x-powered-by):'
# Заглавия на несъществуваща страница
curl -sI https://www.example.de/gibt-es-nicht-4711 | grep -iE '^(server|x-powered-by):'
Очаква се заглавие Server без номер на версия и без X-Powered-By. Ако там все още стои версия на PHP, най-често е активен друг php.ini или някоя услуга още не е рестартирана. Ако пред сървъра има CDN, проверете допълнително директно сървъра на произход, доколкото е достъпен.
Все пак водете версията на PHP вътрешно: във Вашия инвентар на сървърите, със състоянието на пакетите и датата на последната проверка. Състоянието на пачовете доказвате през сървъра, както е описано в Правилна проверка на версията на PHP и на състоянието на пачовете.
Какво носи скриването и какво не
Честно погледнато, изключването на данните за версията е малка мярка с малко усилия. Тя намалява вероятността да се появите в целенасочени търсения на определени версии и намалява информацията, която атакуващият получава без труд.
Тя обаче не защитава от автоматизирани атаки, които просто изпращат заявките си до всеки достъпен сървър. Как протичат такива масови сканирания, описва статията Масови сканирания за непачнат PHP. Срещу PHP2Shell помага само актуализацията до PHP 8.5.12, 8.4.27, 8.3.36 или 8.2.35 респективно потвърден backport, последвана от рестартиране на всички PHP процеси.
Един страничен ефект обаче е изключително полезен: който изключва данните за версията навън, трябва да отчита чисто версиите си вътрешно. В положение като PHP2Shell точно този преглед решава колко бързо ще разберете кои сървъри са засегнати.
Затова скриването има смисъл като част от основното заздравяване, не като единична мярка. То принадлежи в същия контролен списък като изключени изходи на грешки, изтрити тестови файлове и заключени конфигурационни стойности.
Как помага PHP SOS конкретно?
- Отчитане на местата: проверяваме заглавията, страниците с грешки и уеб корена на всички домейни за данни за версии и забравени тестови файлове.
- Адаптиране на конфигурацията: задаваме
expose_php, Server заглавията и филтрите за заглавия във всяка версия на PHP и всеки уеб сървър и рестартираме услугите контролирано. - Вътрешно доказване на състоянието на пачовете: документираме версиите и състоянието на пакетите там, където им е мястото, в инвентара, а не в HTTP заглавието.
- Вграждане в заздравяването: мярката е част от нашето заздравяване на сървъри за PHP уеб сървъри.
Често задавани въпроси
Прави ли expose_php = Off моя сървър сигурен?
Не. Настройката премахва само данните за версията от HTTP заглавието. Уязвима версия на PHP остава уязвима, а автоматизираните атаки често така или иначе изпробват заявките си на всеки достъпен сървър. Скриването обаче отнема на атакуващите лесен предварителен подбор и намалява видимостта на сървъра в търсачките за сървъри.
Мога ли да задам expose_php в пула на FPM или чрез .htaccess?
Според ръководството на PHP expose_php може да се променя само в php.ini. Затова задайте стойността в php.ini на всеки уеб SAPI и след това рестартирайте PHP-FPM или Apache.
Защо виждам X-Powered-By въпреки expose_php = Off?
Тогава най-често самото приложение или рамка задава заглавието, или е активен друг php.ini. Премахнете заглавието допълнително в уеб сървъра: с fastcgi_hide_header в Nginx или Header unset в Apache.
Как да проверя състоянието на пачовете, ако версията вече не се вижда?
Чрез самия сървър: управлението на пакети, changelog на пакета с номерата на CVE и началното време на PHP процесите. Данните в HTTP заглавието така или иначе не бяха доказателство, защото дистрибуциите често внасят backport корекции без нов номер на версията.