Magento server hardening: nginx, PHP-FPM en rechten
Terug naar blog

Magento server hardening: nginx, PHP-FPM en rechten

Bijgewerkt 8 min leestijd

De meeste gehackte Magento-shops zijn niet gehackt door een geniale aanvaller. Er stond een bestand open, een poort publiek of een map beschrijfbaar die dat niet…

De meeste gehackte Magento-shops zijn niet gehackt door een geniale aanvaller. Er stond een bestand open, een poort publiek of een map beschrijfbaar die dat niet hoefde te zijn.

Server hardening is het saaie werk dat een lek in je code een stuk minder duur maakt. Dit artikel loopt langs wat wij bij elke Magento-server controleren.

Wat hardening wel en niet doet

Hardening voorkomt geen kwetsbaarheden in Magento zelf. Als er een RCE in de applicatie zit, zoals bij CosmicSting (CVE-2024-34102) of SessionReaper (CVE-2025-54236), blijft patchen de enige echte oplossing.

Wat hardening wel doet:

  • de schade beperken als een aanvaller binnenkomt;
  • veelvoorkomende aanvalsroutes dichtzetten (uploads uitvoeren, bestanden lezen);
  • voorkomen dat je database, cache en zoekmachine publiek bereikbaar zijn;
  • zorgen dat je merkt wat er gebeurt.

Zie het als een brandwerende deur. Hij voorkomt de brand niet, hij houdt hem op één kamer. Voor de applicatiekant hebben we de Magento 2 security checklist.

1. Webroot: alleen pub/

Dit is de belangrijkste regel en tegelijk de meest overgeslagen. De webserver hoort naar pub/ te wijzen, niet naar de Magento-root.

Wijst je webroot naar de hoofdmap, dan zijn app/etc/env.php, composer.json, var/ en logbestanden potentieel via de browser op te vragen. Dat gaat meestal goed totdat een regel in je nginx-configuratie een keer afwijkt.

nginx
server {
    listen 443 ssl;
    server_name www.voorbeeld.nl;

    set $MAGE_ROOT /var/www/magento;
    root $MAGE_ROOT/pub;

    # rest van de configuratie
}

Magento levert een nginx.conf.sample mee. Gebruik die als startpunt en pas hem aan, in plaats van zelf een config te bouwen. Controleer na elke Magento-upgrade of het voorbeeldbestand is aangepast.

2. Geen PHP uitvoeren in media/ en andere schrijfbare mappen

Veel webshell-aanvallen verlopen zo: de aanvaller krijgt via een kwetsbaarheid een bestand in pub/media en roept het aan. Als nginx daar PHP uitvoert, is het spel gespeeld.

Blokkeer PHP-uitvoer in alle mappen waar de webserver-gebruiker naartoe schrijft:

nginx
location ~ ^/(media|static)/.*\.php$ {
    deny all;
}

location ~ ^/(media|static)/.*\.(phtml|php[0-9]?|phar)$ {
    deny all;
}

Het Magento-voorbeeld bevat al een deel hiervan, maar controleer of het in jouw actieve config staat. Wij testen dit met een onschuldig testbestand: staat er een .php-bestand in media/ dat een tekst teruggeeft, en wordt die uitgevoerd? Dan staat er iets fout.

Blokkeer ook toegang tot dingen die nooit publiek horen te zijn:

nginx
location ~ /\.(git|svn|env|ht) {
    deny all;
}

location ~* \.(sql|bak|old|orig|log|zip|tar|gz)$ {
    deny all;
}

Voor een bredere lijst van zulke fouten, zie ons artikel over security misconfiguration.

3. Bestandsrechten en eigenaarschap

Het principe: de PHP-gebruiker mag alleen schrijven waar Magento echt moet schrijven.

Een veelgebruikte opzet:

  • Eén gebruiker is eigenaar van de code (bijvoorbeeld de deploy-user).
  • PHP-FPM draait onder een andere gebruiker, met alleen schrijfrechten op var/, generated/, pub/media/ en pub/static/ (in productiemode wordt static niet runtime geschreven).
  • Code en configuratie zijn alleen-lezen voor de PHP-gebruiker.

Waarom dit helpt: zelfs als een aanvaller code kan uitvoeren als de PHP-gebruiker, kan hij dan geen bestaande PHP-bestanden in vendor/ of app/ aanpassen. Dat maakt het inbrengen van een backdoor in de kern moeilijker en makkelijker te detecteren.

Wat wij vaak zien: chmod -R 777 "omdat het dan werkt". Doe dat nooit. Het lost een eigenaarsprobleem op met een gat in de beveiliging.

Controleer ook:

  • app/etc/env.php en app/etc/config.php niet wereld-leesbaar;
  • productiemode aan (bin/magento deploy:mode:show);
  • geen .git-map op de productieserver, deploy via artifacts of rsync.

4. PHP-FPM en php.ini

Eigen pool per site

Draai elke webshop in een eigen PHP-FPM-pool met een eigen gebruiker. Delen twee sites een pool, dan kan een gehackte site de bestanden van de ander lezen.

disable_functions: een afweging

Je kunt gevaarlijke functies uitzetten, zoals exec, shell_exec, system, passthru en proc_open. Dat maakt de meeste kant-en-klare webshells onbruikbaar.

Maar let op:

  • Magento zelf en sommige extensies gebruiken een deel van deze functies, bijvoorbeeld voor image-verwerking of PDF-generatie.
  • Een te agressieve lijst geeft kapotte cron-jobs en rare foutmeldingen.

Ons advies: begin met een lijst, test op staging inclusief cron, imports en PDF's, en pas dan toe op productie. Is het een garantie? Nee. Een aanvaller met PHP-uitvoering heeft vaak alternatieven. Het is een extra laag, geen oplossing. Over-engineeren hier levert bij een kleine shop weinig op ten opzichte van de rest van dit artikel.

open_basedir

Met open_basedir beperk je waar PHP bestanden mag lezen. Dat is nuttig als je meerdere sites op één server hebt. Neem de tijdelijke map en de Magento-root op in de lijst, en test of imports, exports en media-uploads nog werken.

Overige instellingen

  • expose_php = Off: verbergt de PHP-versie in headers.
  • display_errors = Off in productie, log_errors = On.
  • allow_url_include = Off (is dat meestal al).
  • Houd PHP zelf bij. Een recent voorbeeld is de PHP-RCE-kwestie die we beschreven in ons artikel over PHP2Shell (CVE-2026-109618 en CVE-2026-109619). Daar helpt geen enkele hardening-instelling tegen: je moet updaten.

5. Redis, OpenSearch en MySQL: nooit publiek

Dit is misschien de duurste fout en ook de meest vermijdbare. Een Redis zonder wachtwoord op een publiek IP is binnen uren gevonden door scanners. Hetzelfde geldt voor OpenSearch/Elasticsearch en MySQL.

Wat dat concreet betekent:

ServiceStandaardpoortWat we instellen
MySQL/MariaDB3306bind-address = 127.0.0.1 of privé-netwerk
Redis6379Alleen localhost of privé-netwerk, wachtwoord, geen FLUSHALL voor onbevoegden
OpenSearch9200Alleen localhost/privé, authenticatie als het netwerk gedeeld is
RabbitMQ5672/15672Alleen intern, beheer-UI niet publiek

Controleer vanaf buiten je netwerk wat er open staat:

bash
nmap -Pn -p 22,80,443,3306,6379,9200,5672,15672 jouw-server-ip

Alleen 80 en 443 (en SSH, zie hieronder) horen er open te staan. Daarnaast zet je een firewall aan die standaard alles blokkeert. Dat is ook een eis bij hosts die je goed kiest: zie onze pagina over Magento hosting.

Staat Redis ook voor sessies? Dan is een open Redis niet alleen dataverlies, maar ook sessie-overname. Dat is exact het type risico dat bij SessionReaper meespeelt.

6. SSH en toegang

  • Alleen SSH-keys, geen wachtwoordlogin (PasswordAuthentication no).
  • Root-login uit (PermitRootLogin no).
  • Eén persoonlijk account per mens, geen gedeelde gebruiker.
  • SSH beperken tot bekende IP's of een VPN, waar praktisch.
  • Verwijder keys van ex-medewerkers en ex-leveranciers. Controleer authorized_keys echt, niet alleen in je hoofd.
  • Een tool als fail2ban voor herhaalde loginpogingen.

Een kleine tip: gebruik aparte keys voor deploys. Een deploy-key die alleen kan deployen is veel minder risicovol dan een persoonlijke sleutel met sudo.

7. TLS en headers

Zet TLS 1.2 en 1.3 aan, oudere protocollen uit. Voeg basisheaders toe:

nginx
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

Let op met HSTS en includeSubDomains: controleer eerst of al je subdomeinen HTTPS aankunnen, anders sluit je jezelf buiten. Magento zet zelf ook al een aantal headers. Voorkom dubbele of tegenstrijdige waarden.

8. Logging, logrotatie en monitoring

Logs zijn je geheugen na een incident. Zonder logs weet je niet hoe de aanvaller binnenkwam.

  • Rouleer logs (logrotate) en bewaar ze lang genoeg om een inbraak van weken terug te kunnen onderzoeken. Hoe lang precies hangt af van je situatie en de AVG.
  • Houd access.log, error.log, PHP-FPM slowlog en var/log apart.
  • Stuur logs bij voorkeur naar een andere plek, zodat een aanvaller ze niet kan wissen.
  • Zet monitoring op bestandswijzigingen in app/, vendor/ en pub/. Een eenvoudige hash-vergelijking tegen je git-repository vangt veel op.

Meer hierover: security logging voor webshops en ons incident response plan.

9. Updates van het besturingssysteem

Een server die al een jaar niet is bijgewerkt, is een server met bekende lekken. Zet automatische security-updates aan voor het OS, plan een maandelijks venster voor kernel-updates en herstarts, en houd een lijst bij van de versies van nginx, PHP, MySQL, Redis en OpenSearch. Zie ook Magento-versies en end of support, want ook onderliggende componenten verlopen.

Hardening-checklist

  • [ ] Webroot is pub/
  • [ ] Geen PHP-uitvoer in media/ en static/
  • [ ] .git, .env, back-ups en logs niet publiek
  • [ ] Code alleen-lezen voor de PHP-gebruiker
  • [ ] Geen chmod 777
  • [ ] Eigen PHP-FPM-pool per site
  • [ ] display_errors uit, expose_php uit
  • [ ] MySQL, Redis, OpenSearch, RabbitMQ alleen intern
  • [ ] Firewall: standaard dicht
  • [ ] SSH met keys, geen root, geen gedeelde accounts
  • [ ] TLS-configuratie en basisheaders gecontroleerd
  • [ ] Logrotatie en externe logopslag
  • [ ] OS-updates en PHP-updates bijgehouden

Veelgestelde vragen

Is disable_functions verplicht voor Magento?

Nee. Het is een extra laag die sommige webshells onbruikbaar maakt, maar het kan ook legitieme functionaliteit breken. Test het op staging inclusief cron, imports en PDF-generatie. Ontbreekt de rest van de hardening, dan levert deze maatregel weinig op.

Waarom moet de webroot pub/ zijn?

Omdat dan alleen bestanden in pub/ via de webserver bereikbaar zijn. Configuratie, broncode, vendor/ en logs liggen buiten de webroot. Een fout in een nginx-regel kan dan niet meteen je env.php blootleggen.

Mag ik Redis of OpenSearch publiek bereikbaar maken voor een koppeling?

Liever niet. Gebruik een privé-netwerk, een VPN of een SSH-tunnel. Moet het publiek, dan met authenticatie, TLS en een IP-allowlist. Voor Redis zonder wachtwoord geldt: nooit publiek.

Hoe vaak moet ik mijn server laten controleren?

Na elke grote wijziging (nieuwe server, migratie, nieuwe PHP-versie) en minimaal jaarlijks. Een security audit kijkt ook naar de serverkant, niet alleen naar code.

Is dit ook van toepassing als mijn hoster alles beheert?

Deels. Een goede managed host regelt OS, firewall en veel PHP-instellingen. Vraag expliciet wat zij doen en wat jij zelf moet doen, bijvoorbeeld nginx-regels, bestandsrechten en toegangsbeheer. Dat grijze gebied is waar het misgaat.

Willen we meekijken naar je server? Wij doen hardening-reviews en beheren Magento-omgevingen via Magento support, met direct contact en zonder tussenlagen. Vraag het via /contact, of lees meer over Magento security.

Ruthger Idema

Geschreven door Ruthger Idema

15+ jaar ervaring in e-commerce development. Gespecialiseerd in Magento, Shopify en Laravel maatwerk.

Meer over ons team →
Deel dit artikel:

Wil je jouw e-commerce naar het volgende niveau?

Plan een vrijblijvend gesprek met onze experts over Magento, Shopify of Laravel maatwerk.

Plan een Tech Check