Kort sagt: För att konfigurera Nginx för PHP säkert lämnar ni bara vidare skript som verkligen finns till PHP-FPM, vid front-controller-applikationer rentav bara index.php. Dit kommer spärrade dotfiler som .git och .env, en standardserver för okända värdnamn, lämpliga storleksgränser och security.limit_extensions = .php i poolen. Det skyddar inte mot sårbarheter i själva PHP men begränsar vad som går att köra efter ett fel.
De farligaste raderna står i PHP-locationen
Sedan PHP2Shell kontrollerar många team sina PHP-FPM-versioner och pooler, som sidan Säkra Nginx med PHP-FPM beskriver, och startar efter uppdateringen om alla workers. En andra blick gäller själva Nginx-konfigurationen. Där avgörs vilka filer som överhuvudtaget körs som PHP.
I många konfigurationer finns ett block som lämnar varje förfrågan mot .php vidare till PHP-FPM, oavsett var filen ligger. Hamnar en inplanterad PHP-fil i en uppladdningsmapp kör servern den. Ett filfel blir så en bakdörr.
Det här inlägget koncentrerar sig på Nginx-sidan: vilka direktiv som räknas, hur ordningen på location-blocken verkar och hur ni testar resultatet.
Hur Nginx avgör vilket block som slår till
Den som vill konfigurera Nginx för PHP säkert måste känna till urvalet av location-block. Enligt Nginx-dokumentationen går det till så här:
- En exakt träff med
location =avslutar sökningen direkt. - Bland prefix-locations noteras den längsta matchande. Bär den
^~kontrolleras inga reguljära uttryck längre. - Annars kontrolleras reguljära uttryck (
~,~*) i konfigurationsfilens ordning; den första träffen vinner. - Träffar inget reguljärt uttryck gäller den noterade prefix-locationen.
Därav följer två praktiska regler. En spärr för PHP i uppladdningsmappar måste antingen läggas som ^~-prefix eller stå som reguljärt uttryck före det allmänna PHP-blocket. Och en vanlig prefix-location som location /uploads/ skyddar inte, eftersom det reguljära uttrycket för .php går före.
En säker grundkonfiguration
Exemplet nedan visar ett server-block för en applikation med front controller, som är vanligt hos Laravel, Symfony, Shopware 6 eller många egenutvecklade lösningar. Sökvägar, socket och certifikat anpassar ni.
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;
# Tillåt Let's Encrypt och andra kända sökvägar
location ^~ /.well-known/ {
try_files $uri =404;
}
# Spärra dolda filer och mappar (.git, .env, .htaccess)
location ~ /\. {
deny all;
}
location / {
try_files $uri $uri/ /index.php$is_args$args;
}
# Bara front controller lämnas vidare till 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;
}
# Varje annan PHP-fil körs inte
location ~ \.php$ {
return 404;
}
}
$realpath_root löser upp symboliska länkar. Det är viktigt vid driftsättningar med en växlande symlänk till den aktuella releasen, så att PHP-FPM och OPcache ser rätt sökväg. Observera dessutom: enligt Nginx-dokumentationen ärvs fastcgi_param-direktiv från högre nivåer bara om det inte finns egna på den aktuella nivån. Därför hör include fastcgi_params; hemma i samma block.
Klassiska CMS som WordPress, TYPO3 eller Contao har flera startpunkter. Där ersätter ni front-controller-blocket med en allmän PHP-location som bara lämnar över existerande filer (try_files $uri =404;), och spärrar körning i alla skrivbara mappar. Hur det lyckas står under Förbjud PHP-körning i uppladdningsmappar.
Det andra skyddet i PHP-FPM
Nginx avgör vad som lämnas vidare, PHP-FPM avgör vad som körs. Enligt PHP-manualen kör PHP-FPM som standard filer med .php och .phar och rekommenderar själv att begränsa det till .php. Det fångar upp konfigurationsfel i webbservern.
; i poolen, t.ex. /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
Socketen bör bara vara nåbar för Nginx. FPM-portar i nätverket, till exempel 9000, ska inte öppnas utåt. Hur servern sammantaget bara visar de nödvändiga portarna beskriver Brandvägg för webbservrar: öppna bara nödvändiga portar.
Okända värdnamn och fler justerskruvar
Utan egen standardserver besvarar Nginx förfrågningar mot okända värdnamn med det första matchande server-blocket. Automatiserade skanningar som bara adresserar IP-adressen hamnar då direkt i er applikation. En egen standardserver avslutar sådana förfrågningar utan svar.
server {
listen 80 default_server;
listen 443 ssl default_server;
server_name _;
ssl_reject_handshake on;
return 444;
}
Fler punkter som har visat sig fungera:
| Justerskruv | Rekommendation | Syfte |
|---|---|---|
server_tokens |
off |
inget versionsnummer i huvuden och på felsidor |
client_max_body_size |
passande till post_max_size |
avvisa stora förfrågningar tidigt |
autoindex |
av (standard) | inga kataloglistor |
fastcgi_hide_header |
X-Powered-By |
ingen PHP-version utåt |
| Statussidor | pm.status_path bara internt |
inga driftdata offentligt |
| Backupfiler | spärra eller ta bort .sql, .zip, .bak i webbroten |
ingen nedladdning av databaser |
Mer om versionsuppgifter står i inlägget expose_php och Server-huvuden.
En ytterligare justerskruv gäller de tillåtna HTTP-metoderna. De flesta webbplatser behöver bara GET, HEAD och POST. Med limit_except går det att avvisa andra metoder i en location; enligt Nginx-dokumentationen tillåter frisläppningen av GET automatiskt även HEAD. Kontrollera först om er applikations gränssnitt använder till exempel PUT eller DELETE, eftersom REST-API:er ofta gör det.
Testa innan ni laddar om
Varje ändring kontrollerar ni först med nginx -t och laddar om Nginx först därefter. Sedan testar ni beteendet utifrån med normala förfrågningar mot er egen domän:
sudo nginx -t && sudo systemctl reload nginx
# Förväntat: 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
# Förväntat: 404, eftersom bara front controller körs
curl -s -o /dev/null -w "%{http_code}\n" https://www.example.de/info.php
# Vilka block lämnar över till PHP-FPM?
sudo nginx -T 2>/dev/null | grep -nE "location|fastcgi_pass"
Säkra före varje större ändring hela utdata från nginx -T med datum. Den innehåller hela den verksamma konfigurationen inklusive alla inkluderade filer. Går något snett efter en ändring visar jämförelsen direkt vad som skiljer sig. Vid nästa kritiska sårbarhet har ni dessutom ett tillförlitligt underlag för inventeringen utan att först behöva leta ihop filer.
Ändra konfigurationen först i en testmiljö eller på en kopia av server-blocket. En bortglömd startpunkt, till exempel en betalnings-callback, märks annars först när beställningarna uteblir.
Hur hjälper PHP SOS konkret?
- Läsa konfigurationen: Vi analyserar hela Nginx-konfigurationen med
nginx -Toch kopplar varje PHP-överlämning till sin webbplats och sin pool. - Fastställa startpunkter: Tillsammans med era utvecklare bestämmer vi vilka skript som måste köras och spärrar resten.
- Genomföra och testa: Vi anpassar server-block och pooler, kontrollerar med
nginx -toch testar formulär, betalvägar och uppladdningar. - Säkra permanent: Konfigurationen blir en del av vår serverhärdning och av dokumentationen av er server.
Vanliga frågor
Måste Nginx startas om efter en PHP-uppdatering?
Inte för PHP-patchen, eftersom Nginx inte innehåller PHP. Alla PHP-FPM-tjänster måste startas om. Ändrar ni samtidigt Nginx-konfigurationen kontrollerar ni den först med nginx -t och laddar sedan om Nginx.
Varför lämna bara index.php vidare till PHP?
Många moderna applikationer har en enda startpunkt, front controller. Lämnas bara den filen till PHP-FPM kan en inplanterad PHP-fil på annat ställe inte köras via webbläsaren. För system med många startpunkter är det mönstret inte möjligt.
Vad ger security.limit_extensions i PHP-FPM?
Inställningen avgör vilka filändelser PHP-FPM kör som skript. Enligt PHP-manualen är standard .php .phar; rekommenderat är att begränsa till .php. Det fångar upp fel i webbserverkonfigurationen men ersätter den inte.
Är Nginx-regler ett skydd mot PHP2Shell?
Nej. PHP2Shell finns i PHP-tolken och stängs bara av uppdateringen till en rättad version och omstart av alla workers. En säker Nginx-konfiguration minskar angreppsytan och begränsar skadan efter ett intrång.