101 —Tooling
phpMyAdmin achter .htpasswd: een veilig copy-paste recept
phpMyAdmin heeft al een eigen login. Een Basic Auth-poort ervoor houdt bots volledig weg van de PHP. Het recept, plus de vier manieren waarop het je buitensluit.
Vrijdagmiddag, je tailt de access log van de shared host waar vier klantsites van je draaien. In het afgelopen uur is /phpmyadmin/index.php 4.200 keer geraakt vanaf 280 verschillende IP's. Geen enkele kwam erdoor, maar ze konden wel de loginpagina laten renderen. De versie fingerprinten. Blijven proberen.
Je beveiligt phpMyAdmin al met de eigen login. Door er een tweede poort voor te zetten (een simpele Basic Auth-challenge via .htaccess en .htpasswd) komen de bots überhaupt niet meer bij de PHP. De PHP-FPM worker wordt nooit gestart, de database nooit bevraagd, de gerenderde HTML gaat nooit terug. Bovendien verberg je zo de phpMyAdmin-versie voor iedereen die loopt te scannen op een bekende CVE.
Het is ook de simpelste manier om jezelf buiten te sluiten op een draaiende legacy site. Dit is het copy-paste recept dat we gebruiken op overgenomen hosts, plus de vier valkuilen waar we vaak genoeg in zijn gestapt om ze uit het hoofd te kennen.
Genereer het wachtwoordbestand buiten de web root
SSH naar de host en draai:
htpasswd -B -c /home/youruser/.pma_htpasswd admin
De flags zijn belangrijk. -B dwingt bcrypt af, en dat is wat je in 2026 wilt; de standaard op oudere Apache-builds is een MD5-achtige variant die op een moderne GPU in seconden gekraakt is. -c maakt het bestand aan. Als het bestand al bestaat en je voegt een tweede gebruiker toe, laat -c dan weg, anders overschrijf je geruisloos het origineel.
Let op het pad. /home/youruser/.pma_htpasswd staat één niveau boven public_html/. Zet het wachtwoordbestand nooit binnen de web root. Doe je dat wel en gaat een verkeerd geconfigureerde server ooit dotfiles serveren, dan lekt de hash. De meeste shared hosts weigeren dotfiles standaard, maar "de meeste" is niet "alle".
Controleer of het bestand leesbaar is voor de webserver-user:
ls -la /home/youruser/.pma_htpasswd
# -rw-r--r-- 1 youruser www-data 67 Jun 10 14:22 .pma_htpasswd
Op cPanel-hosts is het bestand meestal eigendom van je eigen user en group, en draait Apache als diezelfde user via suEXEC. Op een kale Debian-bak draait de webserver als www-data en heeft die leesrechten nodig via group of world. chmod 644 is meestal prima; 640 met de juiste group is strakker.
Het .htaccess-blok, beperkt tot één directory
Zet het volgende in public_html/phpmyadmin/.htaccess. Niet in de site-root. De .htaccess van de site-root is waar WordPress, Joomla of je eigen rewrites staan, en die twee door elkaar halen is precies hoe je de publieke homepage achter een Basic Auth-prompt zet.
AuthType Basic
AuthName "phpMyAdmin"
AuthUserFile /home/youruser/.pma_htpasswd
Require valid-user
# Optioneel: prompt overslaan vanaf een bekend kantoor-IP
# <RequireAny>
# Require ip 203.0.113.42
# Require valid-user
# </RequireAny>
Vier regels, allemaal nodig. De AuthName-string is wat de browser in de inlogprompt toont, dus houd het saai. Schrijf er geen "DB ADMIN" of "MySQL root" in; dat is gratis verkenning voor wie de prompt opent.
Draai je Apache 2.4 (en dat doe je, tenzij de host echt verlaten is), gebruik dan Require valid-user. De oude syntax met Allow from en Order deny,allow is in 2.4 deprecated en in veel distributies verwijderd. Snippets uit tien jaar oude Stack Overflow-antwoorden werken half en falen dan stilletjes bij de volgende serverupgrade. De Apache 2.4 auth howto is de officiële referentie.
De vier manieren waarop dit je buitensluit
Elk van deze is ons al eens overkomen op een echte klantsite.
Verkeerd absoluut pad
Als AuthUserFile verwijst naar een bestand dat niet bestaat, of dat Apache niet kan lezen, krijg je een 500 Internal Server Error zonder bruikbare body en een regel in de error log van de host in de trant van (13)Permission denied: AH01620: Could not open password file. Relatieve paden werken hier niet. Gebruik altijd het absolute pad en check het met realpath /home/youruser/.pma_htpasswd voordat je opslaat.
AllowOverride None op vhost-niveau
Staat in de vhost of de hoofdconfig van Apache AllowOverride None voor je document root, dan wordt het .htaccess-bestand wel gelezen maar genegeerd. Geen error, geen prompt, geen bescherming. Test door het bestand expres kapot te maken (zet het woord banana op regel één) en de pagina opnieuw te laden. Laadt de pagina nog steeds, dan wordt je .htaccess niet gevolgd en moet je de vhost aanpassen of contact opnemen met de hostingmaatschappij. De AllowOverride-documentatie beschrijft per niveau wat er wel en niet mag.
WordPress wp-admin dubbele prompt
Staat phpMyAdmin op /phpmyadmin/ op hetzelfde domein als een WordPress-site, dan zit je goed. Als het om de een of andere reden geneste in de admin-omgeving is beland, triggert elke wp-admin AJAX-call (inclusief de heartbeat) de Basic Auth-prompt en is de back-office voor redacteuren onbruikbaar. Houd phpMyAdmin onder een eigen top-level directory.
HTTP versus HTTPS canonicalisatie
Browsers cachen Basic Auth-credentials per scheme, host, port en realm. Forceert je site een redirect van HTTP naar HTTPS via een andere .htaccess-regel, en de Auth-prompt vuurt vóór de redirect, dan vragen sommige browsers (met name oudere Safari-builds) twee keer om inloggegevens of weigeren ze ze ronduit. Zorg dat de HTTPS-redirect boven het Auth-blok staat in hetzelfde bestand, of in de bovenliggende .htaccess.
Testen zonder de toegang te verliezen
Voordat je de .htaccess opslaat op een productiehost, valideer je het wachtwoordbestand vanaf de command line:
curl -i --user admin:thepassword https://example.com/phpmyadmin/
# HTTP/2 401 (verwacht zodra .htaccess actief is)
# HTTP/2 200 (nadat je de juiste credentials stuurt)
De echte test zit in de volgorde. Houd twee browsertabs open: één in de bestandsbeheerder van je hosting-controlpanel, de ander op de phpMyAdmin-URL. Sla de .htaccess op, ververs de phpMyAdmin-tab en kijk of de inlogprompt verschijnt. Krijg je een 500, dan is de bestandsbeheerder-tab je vluchtroute: hernoem .htaccess naar .htaccess.bak en je bent terug bij af.
Dit is ook de reden dat je nooit de live .htaccess bewerkt via SFTP zonder het origineel open te houden in je editor. Eén verdwaald teken en de hele site geeft 500.
Wat we standaard uitrollen op een legacy site
Op elke overgenomen site die phpMyAdmin op een voorspelbaar pad blootstelt, is onze eerste commit een .htaccess met het blok van vier regels hierboven, plus een directe hernoeming van /phpmyadmin/ naar een minder voor de hand liggende directorynaam. Alleen die hernoeming al haalt zo'n vijfennegentig procent van het botverkeer eruit; de Basic Auth doet de rest.
Toen we Pier bouwden was het herstel-scenario precies wat we steeds tegenkwamen op klantsites. Je zit midden in een config-bestand, er gaat iets mis, de hele site geeft 500, en je moet terug naar de laatste goede versie zonder de editor te verlaten. Hoe we het hebben opgelost: elke opslag van een .htaccess of een rij in de MySQL editor wordt vastgelegd, en version history toont de vorige goede versie met een one-click herstel.
Doe vandaag tenminste dit: open de access log op de meest blootgestelde legacy host, grep op phpmyadmin en tel de hits over de afgelopen vierentwintig uur. Dat getal is het budget voor de volgende twintig minuten werk.
— Vragen —
Waarom Basic Auth toevoegen als phpMyAdmin al een eigen login heeft?
Basic Auth blokkeert requests al op de Apache-laag, waardoor de PHP nooit draait. Bots kunnen de phpMyAdmin-versie niet fingerprinten, en een 0-day in de applicatie zelf is onbereikbaar zonder geldige credentials.
Moet ik bcrypt of MD5 gebruiken voor het .htpasswd-bestand?
Bcrypt, via de -B flag van htpasswd. MD5-achtige hashes zijn op een moderne GPU in seconden te kraken. Bcrypt met een redelijke cost factor blijft jaren bestand tegen brute force.
Wat doe ik bij een 500-error direct na het opslaan van de .htaccess?
Check de error log van de host op een AH01620-regel. Vrijwel altijd betekent dit dat het AuthUserFile-pad fout staat, buiten het leesbare bereik wijst, of verkeerde permissies heeft. Hernoem de .htaccess om weer toegang te krijgen, en herstel daarna het pad.