Kort gezegd: Bij Nginx met PHP-FPM zit de kwetsbaarheid van PHP2Shell in PHP-FPM, niet in Nginx. Patch alle PHP-FPM-versies, herstart elke FPM-dienst volledig en onderbouw dat met starttijden van processen. Voor een blijvende Nginx PHP-FPM beveiliging tellen gescheiden pools, niet-overschrijfbare waarden en een Nginx-configuratie die geen geüploade bestanden uitvoert.
Hoe Nginx en PHP-FPM samenwerken
Nginx levert statische bestanden zelf uit en geeft verzoeken aan PHP-scripts via FastCGI door aan PHP-FPM. PHP-FPM bestaat uit een masterproces en meerdere workerprocessen, gegroepeerd in pools. Een worker handelt achter elkaar veel verzoeken af.
Precies dat is voor PHP2Shell relevant. Bij CVE-2026-109619 kan de interne toestand van een verzoek na het vrijgeven van het geheugen blijven bestaan en door een later verzoek in dezelfde worker worden gebruikt. Bij CVE-2026-109618 kan een out-of-memory-toestand PHP midden in de initialisatie van een verzoek onderbreken. Beide kwetsbaarheden zijn beoordeeld met CVSS v4.0 9.2 en verholpen in PHP 8.5.12, 8.4.27, 8.3.36 en 8.2.35.
Inventaris controleren: versies, pools, processen
Veel servers hebben meerdere PHP-FPM-versies naast elkaar. De volgende opdrachten lezen alleen; pakket- en dienstnamen zijn voorbeelden voor Debian/Ubuntu en de RHEL-familie.
# Welke FPM-diensten draaien?
systemctl list-units --type=service | grep -i fpm
# Welke versie zit erachter? (voorbeeld Debian/Ubuntu)
php-fpm8.3 -v
# RHEL-familie
php-fpm -v
# Naar welke sockets of poorten geeft Nginx door?
nginx -T 2>/dev/null | grep -E 'fastcgi_pass'
# Pools en hun gebruikers
grep -RhE '^\[|^user|^listen' /etc/php/*/fpm/pool.d/ 2>/dev/null
Vergelijk elke fastcgi_pass-vermelding met een draaiende pool. Zo ziet u welke PHP-versie welke website bedient. Bij distributiepakketten onderbouwt het wijzigingslogboek met de CVE-nummers de patch, niet het versienummer alleen.
Een veelvoorkomende bevinding bij zulke inventarisaties zijn servers waarop in de loop der jaren meerdere PHP-versies zijn geïnstalleerd, waarvan er maar één echt in gebruik is. De overige FPM-diensten blijven toch draaien, vaak met standaardpools die luisteren op een lokale socket of poort. Ze zijn van buitenaf meestal niet rechtstreeks bereikbaar, maar blijven een onnodig risico en verlengen elke noodupdate. Schakel versies die geen enkele website gebruikt uit en verwijder ze, nadat u de toewijzing via de fastcgi_pass-vermeldingen hebt gecontroleerd.
Ook loont een blik op de gebruiker van de pools. Draaien alle pools als www-data of nginx, dan bestaat de scheiding tussen de websites alleen op papier. Wie één website overneemt, kan dan de bestanden van alle andere lezen, bijvoorbeeld configuratiebestanden met databasewachtwoorden. Eigen systeemgebruikers per pool en passende bestandsrechten scheppen hier een echte grens. Dat is geen oplossing voor PHP2Shell, maar het beperkt de mogelijke schade van een geslaagde aanval aanzienlijk.
Patch doorvoeren en alle workers vernieuwen
Herstart na de pakketupdate elke PHP-FPM-dienst. Een reload vervangt weliswaar de workers, maar een volledige herstart vernieuwt ook het masterproces en is na beveiligingsupdates de veilige weg.
# Voorbeeld Debian/Ubuntu, per geïnstalleerde versie
systemctl restart php8.3-fpm
# RHEL-familie
systemctl restart php-fpm
# Controle: starttijd van master en workers
ps -eo pid,lstart,cmd | grep -E 'php-fpm' | grep -v grep
Elk proces moet na de update zijn gestart. Uitvoerig beschreven staat dat onder PHP-FPM en alle workers herstarten.
Een patch verwijdert geen schadelijke software die eerder is ingebracht. Bewaar de toegangs- en foutlogs van Nginx vanaf 20-10-2026 en controleer de websites op sporen.
Overbruggingswaarden in de pool instellen
Zolang een versie niet is gepatcht, adviseert php.net overbruggingsmaatregelen. Ze bieden geen volledige bescherming. In PHP-FPM stelt u de waarden in met php_admin_value respectievelijk php_admin_flag in de pool, dan kunnen toepassingen ze niet via ini_set() overschrijven.
; Voorbeeld: /etc/php/8.3/fpm/pool.d/website.conf
php_admin_flag[session.upload_progress.enabled] = off
php_admin_value[post_max_size] = 32M
php_admin_value[upload_max_filesize] = 24M
Zonder uploadfunctie noemt php.net post_max_size = 8M. Op PHP 8.5 komt fatal_error_backtraces=0 erbij. Voor de aanwijzing over memory_limit=-1 hebt u een geheugengrens op cgroup-niveau nodig, bijvoorbeeld via MemoryMax in een systemd-aanvulling voor de FPM-dienst. Pas in Nginx client_max_body_size aan op de nieuwe post_max_size, zodat gebruikers begrijpelijke foutmeldingen krijgen. Herstart daarna FPM.
Nginx PHP-FPM beveiliging duurzaam verbeteren
Naast het acute geval helpen enkele basisregels die de schade bij toekomstige kwetsbaarheden beperken:
- Eén pool per website met een eigen systeemgebruiker, eigen socket en eigen
open_basedir-grens. Meer onder open_basedir en isolatie per website. - Socketrechten zo instellen dat alleen Nginx erbij kan, geen FPM-poorten naar buiten openen.
- Alleen bestaande scripts uitvoeren: zet in de PHP-location
try_files $uri =404;, zodat Nginx geen paden aan PHP doorgeeft die niet bestaan. - Uploads niet uitvoeren: verbied PHP-uitvoering in uploadmappen. Aanwijzingen daarvoor staan onder Bestandsuploads veilig implementeren.
- Statuspagina's beschermen: maak
pm.status_pathenping.pathalleen intern bereikbaar.
# Voorbeeld: geen PHP-uitvoering in de uploadmap
location ~* ^/uploads/.*\.php$ {
deny all;
}
Controleer elke wijziging met nginx -t voordat u Nginx herlaadt. Leg bovendien vast welke pools, versies en bijzondere regels er op de server bestaan. Deze korte documentatie bespaart bij de volgende kritieke kwetsbaarheid kostbare tijd, omdat niemand eerst hoeft uit te zoeken welke website bij welke pool hoort.
Hoe helpt PHP SOS concreet?
- Wij koppelen elke website aan haar pool en PHP-versie en controleren de werkelijke pakketstatus.
- Wij patchen, herstarten alle FPM-diensten en documenteren starttijden van processen als bewijs.
- Tot de patch stellen we de overbruggingswaarden per pool in en beperken we het geheugen via cgroup.
- In het kader van de serverhardening scheiden we pools, rechten en uploadmappen netjes.
- Bij vermoeden van een inbraak nemen we de Incident Response over.
Veelgestelde vragen
Volstaat systemctl reload voor PHP-FPM na een update?
Een reload vernieuwt de workers, maar het masterproces kan de oude code behouden. Na een beveiligingsupdate is een volledige herstart van de dienst de veilige weg. Controleer daarna de starttijd van master en workers.
Moet Nginx na een PHP-update worden herstart?
Nginx zelf bevat geen PHP en hoeft voor de PHP-patch niet te herstarten. Herstarten moeten alle PHP-FPM-diensten. Wijzigt u daarbij de Nginx-configuratie, controleer die dan vóór het herladen met nginx -t.
Waarom aparte pools per website?
Elke pool draait met een eigen gebruiker en eigen grenzen. Wordt één website gecompromitteerd, dan komt de aanvaller niet zomaar bij de bestanden van de andere. Bovendien kunt u overbruggingswaarden gericht per pool instellen.
Is pm.max_requests een bescherming tegen PHP2Shell?
Nee, het vervangt geen patch. De waarde begrenst hoeveel verzoeken een worker afhandelt voordat hij wordt vervangen. Dat kan de levensduur van toestanden in het proces verkorten, maar is geen door php.net genoemde oplossing.