Naar de inhoud
Server & .htaccess

WordPress .htaccess-beveiligingsregels die echt helpen

Het meeste wat als WordPress .htaccess-beveiliging wordt verkocht is opvulling. De regels die je aanvalsoppervlak echt veranderen zijn kort — hier lees je wat wat is

Gepubliceerd

Het meeste wat als “WordPress .htaccess-beveiliging” wordt verkocht is opvulling. De regels die je aanvalsoppervlak echt veranderen zijn kort: blokkeer PHP-uitvoering binnen wp-content/uploads, weiger directe toegang tot wp-config.php, en schakel directory indexing uit. De populaire regels — xmlrpc.php blokkeren “tegen brute force”, je WordPress-versie verbergen, een lijst van 200 regels met kwaadaardige bot-user-agents plakken — variëren van marginaal tot pure schijnvertoning. Hieronder lees je wat wat is, en waarom.

Eerst iets belangrijks: .htaccess doet alleen iets op Apache (en LiteSpeed, dat het ook leest). Op nginx wordt het volledig genegeerd — het bestand staat er gewoon terwijl je denkt dat je beschermd bent. Draait je host op nginx, dan geldt niets hiervan en heb je in plaats daarvan server/location-blokken nodig. Controleer het met curl -I https://jouwsite.com en kijk naar de Server:-header voordat je een uur besteedt aan het bewerken van een bestand dat de server nooit leest.

De regel die echt telt: geen PHP in /uploads

Dit is degene die de moeite waard is. wp-content/uploads is standaard beschrijfbaar voor iedereen — elke media-upload, elke plugin die een bestand opslaat, schrijft daarheen. Als een aanvaller een .php-bestand in die map krijgt (via een kwetsbare upload-handler, een afbeeldingsveld dat het MIME-type niet valideert, een gecompromitteerde plugin), dan zit het verschil tussen een ergernis en een volledige remote-code-execution-compromittering in of de server dat bestand uitvoert wanneer het wordt opgevraagd. Weiger uitvoering en de geüploade payload is niets meer dan een inert bestand op schijf.

Zet dit in wp-content/uploads/.htaccess (maak het bestand aan als het niet bestaat):

<FilesMatch "\.php$">
    Require all denied
</FilesMatch>

Dat is Apache 2.4-syntax. Op het oudere 2.2 is het equivalent Order Deny,Allow / Deny from all. Het door elkaar halen van beide dialecten in één bestand is de meest voorkomende oorzaak van een plotselinge 500 Internal Server Error na het “hardenen” — als de hele site crasht op het moment dat je opslaat, is dat vrijwel altijd de reden. Controleer je versie met apachectl -v.

Bescherm wp-config.php

wp-config.php bevat je databasegegevens en auth salts. Zolang PHP draait, geeft een directe aanvraag naar dit bestand een lege pagina terug — PHP voert het bestand uit in plaats van het te tonen. Het risico zit in de faalsituatie: als PHP ooit crasht, verkeerd geconfigureerd raakt tijdens een migratie, of de handler wordt uitgeschakeld, dan serveert Apache het bestand als platte tekst en dumpt het je DB-wachtwoord aan iedereen die erom vraagt. Toegang weigeren is een goedkope verzekering tegen vijf slechte minuten:

<Files wp-config.php>
    Require all denied
</Files>

Zet directory indexing uit

Als iemand een map bezoekt zonder index.php en Apache heeft Options +Indexes aanstaan, dan toont het de inhoud — elk back-upbestand, elke vergeten SQL-dump. Zet het site-breed uit:

Options -Indexes

Lage ernst, maar reëel, en het kost niets.

Dat is de eerlijke kernlijst. Je kunt deze regels samenstellen — inclusief de juiste 2.4-vs-2.2-syntax zodat je je site geen 500 geeft — met de WordPress .htaccess-generator in plaats van kopiëren en plakken uit een forumbericht dat voor de verkeerde Apache-versie is geschreven.

Wat je NIET moet doen

xmlrpc.php blokkeren “voor brute-forcebescherming”. Dit is de grote die iedereen napraat en zoals gesteld is het gewoon fout. Ja, XML-RPC’s system.multicall-methode liet een aanvaller vroeger vele inlogpogingen in één enkele aanvraag bundelen — echte amplificatie. Maar de veruit meest voorkomende brute-forcevector is gewone POST-aanvragen naar wp-login.php, en xmlrpc.php blokkeren doet daar niets tegen. Brute force versla je met rate limiting, sterke wachtwoorden en 2FA — niet door één endpoint uit te schakelen. Er is een legitieme reden om XML-RPC uit te schakelen: de pingback-functie kan worden misbruikt voor DDoS-reflectie, dus als je geen Jetpack, de mobiele app of pingbacks gebruikt, verkleint het afsluiten ervan je aanvalsoppervlak. Maak jezelf alleen niet wijs dat het je brute-forceverdediging is, want dat is het niet.

Je WordPress-versie verbergen / de generator-tag verwijderen. Het strippen van readme.html en de <meta name="generator">-tag voelt als hardenen. Een aanvaller vingerafdrukt je versie in seconden uit query strings van enqueued assets, block-editor-markup en een dozijn andere verklikkers. Je verbergt niets; je maakt jezelf alleen wijs dat je bezig bent.

Reusachtige blocklists met kwaadaardige bot-user-agents en referrers. User agents zijn één enkele vervalste HTTP-header. Deze lijsten zijn verouderd op de dag dat je ze plakt, ze blokkeren niets competents, en Apache evalueert elke regex op elke aanvraag — je betaalt een reële prestatiebelasting voor nul beveiliging. Sla ze over.

wp-login.php op IP vergrendelen. Prima totdat je ISP je adres roteert en je jezelf hebt buitengesloten van je eigen beheer. Alleen haalbaar met een echt statisch IP.

Auteur-enumeratie-redirects (?author=1). De .htaccess-rewrite die mensen hiervoor plakken is op zichzelf onvolledig — het REST-endpoint /wp-json/wp/v2/users toont nog steeds gebruikersnamen. Eén pad blokkeren terwijl het andere openstaat is schijnvertoning.

Nog steeds vast?

Geeft een regel je site een 500, dan is het syntax — verwijder het laatste blok dat je toevoegde en herlaad; dat isoleert het meteen. Lijkt een regel niets te doen, controleer dan of je echt op Apache zit en dat AllowOverride is ingeschakeld voor de map (veel managed hosts beperken dit). Bouw het bestand vanaf een bekende, werkende template met de .htaccess-generator, houd de drie regels die ertoe doen, en laat de rest weg.