Kort sagt: For at konfigurere Nginx sikkert til PHP giver I kun scripts videre til PHP-FPM, som virkelig findes, ved front-controller-applikationer endda kun index.php. Hertil kommer spærrede dotfiles som .git og .env, en standardserver til ukendte værtsnavne, passende størrelsesgrænser og security.limit_extensions = .php i poolen. Det beskytter ikke mod sårbarheder i selve PHP, men begrænser, hvad der kan udføres efter en fejl.
De farligste linjer står i PHP-location'en
Siden PHP2Shell kontrollerer mange teams deres PHP-FPM-versioner og pools, som siden Afsikr Nginx med PHP-FPM beskriver, og genstarter efter opdateringen alle workers. Et andet blik gælder selve Nginx-konfigurationen. Dér afgøres det, hvilke filer der overhovedet udføres som PHP.
I mange konfigurationer står der en blok, der sender hver forespørgsel til .php videre til PHP-FPM, uanset hvor filen ligger. Havner en indsmuglet PHP-fil i en upload-mappe, udfører serveren den. Af en filfejl bliver der således en bagdør.
Dette indlæg koncentrerer sig om Nginx-siden: hvilke direktiver der tæller, hvordan rækkefølgen af location-blokke virker, og hvordan I tester resultatet.
Sådan afgør Nginx, hvilken blok der gælder
Den, der vil konfigurere Nginx sikkert til PHP, må kende udvælgelsen af location-blokke. Ifølge Nginx-dokumentationen forløber den således:
- Et nøjagtigt match med
location =afslutter søgningen med det samme. - Blandt præfiks-locations huskes den længste passende. Bærer den
^~, kontrolleres ingen regulære udtryk mere. - Ellers kontrolleres regulære udtryk (
~,~*) i konfigurationsfilens rækkefølge; det første match vinder. - Passer intet regulært udtryk, gælder den huskede præfiks-location.
Heraf følger to praktiske regler. En spærring for PHP i upload-mapper skal enten oprettes som ^~-præfiks eller stå som regulært udtryk før den generelle PHP-blok. Og en normal præfiks-location som location /uploads/ beskytter ikke, fordi det regulære udtryk for .php overtrumfer den.
En sikker grundkonfiguration
Eksemplet nedenfor viser en server-blok til en applikation med front-controller, som den er almindelig ved Laravel, Symfony, Shopware 6 eller mange egenudviklinger. Stier, socket og certifikater tilpasser I.
server {
listen 443 ssl;
server_name www.example.de;
root /var/www/seite1/public;
index index.php;
ssl_certificate /etc/ssl/seite1/fullchain.pem;
ssl_certificate_key /etc/ssl/seite1/privkey.pem;
server_tokens off;
client_max_body_size 32m;
# Tillad Let's Encrypt og andre kendte stier
location ^~ /.well-known/ {
try_files $uri =404;
}
# Spær skjulte filer og mapper (.git, .env, .htaccess)
location ~ /\. {
deny all;
}
location / {
try_files $uri $uri/ /index.php$is_args$args;
}
# Kun front-controlleren overdrages til PHP-FPM
location = /index.php {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $realpath_root;
fastcgi_hide_header X-Powered-By;
fastcgi_pass unix:/run/php/seite1.sock;
}
# Enhver anden PHP-fil udføres ikke
location ~ \.php$ {
return 404;
}
}
$realpath_root opløser symbolske links. Det er vigtigt ved deployments med skiftende symlink til den aktuelle release, så PHP-FPM og OPcache ser den rigtige sti. Bemærk desuden: Ifølge Nginx-dokumentationen nedarves fastcgi_param-direktiver kun fra højere niveauer, hvis der ikke står egne på det aktuelle niveau. Derfor hører include fastcgi_params; hjemme i den samme blok.
Klassiske CMS som WordPress, TYPO3 eller Contao har flere indgangspunkter. Dér erstatter I front-controller-blokken med en generel PHP-location, der kun overdrager eksisterende filer (try_files $uri =404;), og spærrer udførelsen i alle skrivbare mapper. Hvordan det lykkes, står under Forbyd PHP-udførelse i upload-mapper.
Den anden sikring i PHP-FPM
Nginx afgør, hvad der gives videre, PHP-FPM afgør, hvad det udfører. Ifølge PHP-manualen udfører PHP-FPM som standard filer med .php og .phar og anbefaler selv at begrænse det til .php. Det opfanger konfigurationsfejl i webserveren.
; i poolen, f.eks. /etc/php/8.3/fpm/pool.d/seite1.conf
security.limit_extensions = .php
listen = /run/php/seite1.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
Socketten bør kun kunne nås af Nginx. FPM-porte i netværket, f.eks. 9000, hører ikke hjemme åbnet udadtil. Hvordan serveren samlet kun viser de nødvendige porte, beskriver Firewall til webservere: Åbn kun nødvendige porte.
Ukendte værtsnavne og flere justeringsskruer
Uden egen standardserver besvarer Nginx forespørgsler til ukendte værtsnavne med den første passende server-blok. Automatiserede scanninger, der kun henvender sig til IP-adressen, havner så direkte i jeres applikation. En egen standardserver afslutter sådanne forespørgsler uden svar.
server {
listen 80 default_server;
listen 443 ssl default_server;
server_name _;
ssl_reject_handshake on;
return 444;
}
Yderligere punkter, der har vist sig at fungere:
| Justeringsskrue | Anbefaling | Formål |
|---|---|---|
server_tokens |
off |
intet versionsnummer i header og fejlsider |
client_max_body_size |
passende til post_max_size |
afvis store forespørgsler tidligt |
autoindex |
fra (standard) | ingen mappelister |
fastcgi_hide_header |
X-Powered-By |
ingen PHP-version udadtil |
| Statussider | pm.status_path kun internt |
ingen driftsdata offentligt |
| Backupfiler | spær eller fjern .sql, .zip, .bak i webroden |
ingen download af databaser |
Mere om versionsangivelser står i indlægget expose_php og Server-headeren.
En yderligere justeringsskrue angår de tilladte HTTP-metoder. De fleste websites har kun brug for GET, HEAD og POST. Med limit_except kan andre metoder afvises i en location; ifølge Nginx-dokumentationen tillader frigivelsen af GET automatisk også HEAD. Kontrollér på forhånd, om grænseflader i jeres applikation bruger f.eks. PUT eller DELETE, for REST-API'er gør det ofte.
Test, før I genindlæser
Hver ændring kontrollerer I først med nginx -t og genindlæser først derefter Nginx. Bagefter tester I adfærden udefra med normale forespørgsler til jeres eget domæne:
sudo nginx -t && sudo systemctl reload nginx
# Forventet: 403 eller 404
curl -s -o /dev/null -w "%{http_code}\n" https://www.example.de/.git/HEAD
curl -s -o /dev/null -w "%{http_code}\n" https://www.example.de/.env
# Forventet: 404, fordi kun front-controlleren udføres
curl -s -o /dev/null -w "%{http_code}\n" https://www.example.de/info.php
# Hvilke blokke overdrager til PHP-FPM?
sudo nginx -T 2>/dev/null | grep -nE "location|fastcgi_pass"
Sikr før hver større ændring det fuldstændige output fra nginx -T med dato. Det indeholder hele den virksomme konfiguration inklusive alle indbundne filer. Går noget galt efter en ændring, viser sammenligningen straks, hvad der er forskelligt. Ved næste kritiske sårbarhed har I desuden et pålideligt grundlag for beholdningsopgørelsen uden først at skulle samle filer sammen.
Ændr først konfigurationen i et testmiljø eller på en kopi af server-blokken. Et glemt indgangspunkt, f.eks. et betalings-callback, opdages ellers først, når ordrer udebliver.
Hvordan hjælper PHP SOS konkret?
- Læs konfigurationen: Vi analyserer hele Nginx-konfigurationen med
nginx -Tog henfører hver PHP-overdragelse til sit website og sin pool. - Fastlæg indgangspunkter: Sammen med jeres udviklere bestemmer vi, hvilke scripts der skal udføres, og spærrer resten.
- Gennemfør og test: Vi tilpasser server-blokke og pools, kontrollerer med
nginx -tog tester formularer, betalingsveje og uploads. - Afsikr varigt: Konfigurationen bliver en del af vores serverhærdning og dokumentationen af jeres server.
Ofte stillede spørgsmål
Skal Nginx genstartes efter en PHP-opdatering?
Ikke for PHP-patchen, for Nginx indeholder ikke PHP. Genstartes skal alle PHP-FPM-tjenester. Ændrer I samtidig Nginx-konfigurationen, kontrollerer I den først med nginx -t og genindlæser derefter Nginx.
Hvorfor kun give index.php videre til PHP?
Mange moderne applikationer har ét enkelt indgangspunkt, front-controlleren. Overdrages kun denne fil til PHP-FPM, kan en indsmuglet PHP-fil et andet sted ikke udføres via browseren. Ved systemer med mange indgangspunkter er dette mønster ikke muligt.
Hvad giver security.limit_extensions i PHP-FPM?
Indstillingen fastlægger, hvilke filendelser PHP-FPM udfører som script. Ifølge PHP-manualen er standarden .php .phar; anbefalet er at begrænse til .php. Det opfanger fejl i webserverkonfigurationen, men erstatter den ikke.
Er Nginx-regler en beskyttelse mod PHP2Shell?
Nej. PHP2Shell sidder i PHP-fortolkeren og lukkes kun af opdateringen til en rettet version og genstart af alle workers. En sikker Nginx-konfiguration formindsker angrebsfladen og begrænser skaden efter et indbrud.