Kort gezegd: Een .env-bestand beveiligt u door het in de eerste plaats niet in de openbaar bereikbare map te zetten: de webroot wijst naar een submap zoals public/, de configuratie staat een niveau hoger. Daarnaast blokkeert de webserver alle bestanden met een punt vooraan, is het bestand alleen leesbaar voor eigenaar en PHP-proces en hoort het nooit in de Git-repository. Na een inbraak gelden alle daarin opgenomen toegangsgegevens als bekend en worden ze vervangen.
Het bestand met de sleutels van het huis
Moderne PHP-projecten bewaren hun geheimen graag in een bestand met de naam .env: databasewachtwoord, toegang tot de mailserver, API-sleutels voor betaal- en verzenddiensten, de sleutel waarmee de applicatie cookies ondertekent. Frameworks zoals Laravel en Symfony gebruiken dit patroon, veel eigen ontwikkelingen hebben het overgenomen.
Het idee is goed: configuratie en code zijn gescheiden en elke omgeving krijgt eigen waarden. Gevaarlijk wordt het als het bestand in de verkeerde map terechtkomt. Staat het in de webroot en blokkeert de webserver het niet, dan volstaat één enkele aanroep in de browser om alle toegangen in één keer te lezen.
Daarbij komen varianten met andere namen: .env.local, .env.production, config.local.php of bestanden met toegangsgegevens tot opslagdiensten. Wat hier voor .env wordt beschreven, geldt voor alle bestanden waarin geheimen staan.
Zulke bestanden worden voortdurend door geautomatiseerde scanners gezocht. Dat is geen specialiteit van afzonderlijke aanvallers, maar achtergrondruis op het internet, vergelijkbaar met de massascans naar niet-gepatchte PHP-versies.
Waar het .env-bestand hoort te staan
De effectiefste maatregel is structureel: de webserver mag alleen de map uitleveren die echt openbaar moet zijn. In Laravel en Symfony is dat de map public/. Al het andere, dus code, configuratie, logboeken en .env, staat daarboven en is via HTTP helemaal niet adresseerbaar.
/srv/www/shop/
├── .env ← niet bereikbaar
├── config/
├── src/
├── vendor/
└── public/ ← enige webroot
├── index.php
└── assets/
In de praktijk wijst de webroot toch vaak naar de projectmap zelf, bijvoorbeeld omdat een hostingpaneel dat zo voorschrijft of omdat bij een verhuizing een pad niet is aangepast. Controleer daarom in de webserverconfiguratie of in het paneel waarheen root respectievelijk DocumentRoot wijst.
.env-bestand beveiligen: blokkade in de webserver als tweede linie
Laat de webroot zich niet wijzigen, of wilt u een extra beveiliging, blokkeer dan verborgen bestanden in de webserver. De map .well-known moet daarbij bereikbaar blijven, omdat die onder meer wordt gebruikt voor het uitgeven van certificaten.
# Alle bestanden en mappen met een punt vooraan blokkeren, behalve .well-known
location ~ /\.(?!well-known/) {
deny all;
return 404;
}
# Bestanden met een punt vooraan blokkeren
<FilesMatch "^\.">
Require all denied
</FilesMatch>
Een antwoord met 404 in plaats van 403 verraadt niet dat het bestand bestaat. Of de blokkade werkt, controleert u van buitenaf:
# Verwacht: 403 of 404, nooit 200
curl -s -o /dev/null -w "%{http_code}\n" https://www.example.de/.env
curl -s -o /dev/null -w "%{http_code}\n" https://www.example.de/.env.example
curl -s -o /dev/null -w "%{http_code}\n" https://www.example.de/.env.bak
De laatste twee regels zijn geen toeval: kopieën zoals .env.bak of .env.old ontstaan bij onderhoudswerk en worden vaak vergeten. Meer over zulke overblijfselen staat in Back-upbestanden in de webroot.
Rechten, Git en de omgang met kopieën
Ook buiten de webroot geldt het principe van de minste rechten. Een beproefde indeling:
- Eigenaar is de deploygebruiker, niet het PHP-proces.
- Groep is de groep van de PHP-FPM-pool, die alleen mag lezen; rechten
640. - Geen schrijftoegang voor het PHP-proces. Een applicatie hoeft haar eigen toegangsgegevens niet te kunnen wijzigen.
- Geen echte waarden in de repository: alleen
.env.examplemet plaatshouders wordt ingecheckt, het echte bestand staat in.gitignore. - Gescheiden waarden per omgeving: ontwikkeling, staging en productie gebruiken verschillende toegangen, zodat een lek in een testomgeving de productie niet opent.
chown deploy:shop-php /srv/www/shop/.env
chmod 640 /srv/www/shop/.env
# Controleren of het bestand ooit is ingecheckt
git log --all --oneline -- .env
Let ook op foutpagina's. Frameworks tonen in de debugmodus soms omgevingsvariabelen en configuratiewaarden; op een productiesysteem moet deze modus zijn uitgeschakeld. Controleer dat na elke deployment, want een verkeerde waarde in de .env zelf kan hem weer inschakelen.
Levert de laatste opdracht treffers op, dan staan de toegangsgegevens in de Git-geschiedenis en gelden ze als blootgelegd, ook als het bestand er vandaag niet meer is. Waarom dat bijzonder gevoelig is als de .git-map op de server staat, beschrijft Openbare .git-map: een onderschat datalek. Basisprincipes over rechten legt de gids Bestandsrechten op de webserver uit.
Testbestanden met phpinfo() tonen omgevingsvariabelen. Wie geheimen als omgevingsvariabelen doorgeeft, verraadt ze via een vergeten testbestand. Achtergronden staan in phpinfo()-bestanden: waarom ze moeten verdwijnen.
Na een inbraak: toegangsgegevens vervangen
Geen blokkade beschermt tegen een aanvaller die al code op de server uitvoert. Een PHP-proces moet zijn configuratie kunnen lezen, dus binnengesmokkelde code in hetzelfde proces kan dat ook. Na PHP2Shell geldt daarom: kan een inbraak niet worden uitgesloten, behandel dan alle toegangsgegevens op de server als bekend.
Een ordelijke vervanging omvat databasewachtwoorden, mail- en SMTP-toegangen, API-sleutels van externe diensten, ondertekeningssleutels van de applicatie en toegangen tot opslagdiensten. Volgorde en tijdstip moeten worden gepland: wie eerst vervangt en dan de server opschoont, geeft de aanvaller mogelijk meteen de nieuwe waarden mee.
De schade van een lek beperkt u al vooraf, doordat elk geheim alleen zoveel mag als nodig is. De databasegebruiker van de website heeft geen rechten op andere databases nodig. API-sleutels van externe diensten zijn bij veel aanbieders te beperken tot afzonderlijke functies of afzenderadressen, bijvoorbeeld zendingen aanmaken maar geen uitbetalingen initiëren. En elke applicatie krijgt eigen toegangen, in plaats van dat meerdere websites hetzelfde mailaccount of dezelfde sleutel delen. Dan betreft een lek een duidelijk afgebakend gebied en blijft de vervanging overzichtelijk.
In containeromgevingen hoort het .env-bestand noch in de image noch in een openbaar bereikbare registry. Sluit het uit via .dockerignore en geef geheimen pas tijdens de uitvoering door via de mechanismen van het platform.
Op de lange termijn loont een blik op centrale secretopslagen, zoals OWASP ze adviseert: geheimen op één plek, met toegangslogboek, regelmatige vervanging en een zo kort mogelijke geldigheid.
Hoe helpt PHP SOS concreet?
- Wij controleren van buitenaf of configuratiebestanden, kopieën en verborgen bestanden van uw websites op te halen zijn.
- Wij zetten webroot, webserverblokkades en bestandsrechten zo om dat geheimen niet meer via HTTP bereikbaar zijn.
- Wij controleren repositories en deployments op ingecheckte toegangsgegevens.
- Na een incident plannen en begeleiden wij de vervanging van alle getroffen toegangen in de juiste volgorde.
Deze werkzaamheden zijn onderdeel van onze serverhardening voor PHP-webservers.
Veelgestelde vragen
Is een .env-bestand in de webroot automatisch openbaar?
Niet automatisch, maar vaak wel. Of het op te halen is, hangt af van de webserverconfiguratie. Veel standaardconfiguraties beschermen alleen .htaccess-bestanden, niet andere bestanden met een punt vooraan. Een test met curl brengt duidelijkheid.
Zijn omgevingsvariabelen veiliger dan een .env-bestand?
Niet principieel. OWASP wijst erop dat omgevingsvariabelen voor alle processen van de gebruiker leesbaar zijn en in logboeken of geheugendumps kunnen opduiken. Ze vermijden wel een bestand in de webroot. Beter zijn centrale secretopslagen, waar de omgeving die ondersteunt.
Wat te doen als het .env-bestand op te halen was?
Alle daarin opgenomen toegangsgegevens als bekend beschouwen en vervangen: database, mail, API's, betaaldiensten, sleutels van de applicatie. Controleer daarna in de toegangslogboeken of het bestand is opgehaald en betrek bij persoonsgegevens de functionaris voor gegevensbescherming.
Hoort het .env-bestand in de Git-repository?
Nee. In de repository hoort hooguit een sjabloon zonder echte waarden, bijvoorbeeld .env.example. Het echte bestand wordt via .gitignore uitgesloten en apart op de server geplaatst.
Kan phpinfo() toegangsgegevens verraden?
Ja. phpinfo() toont onder meer omgevingsvariabelen en servervariabelen. Staan er geheimen in, dan worden ze mee weergegeven. Testbestanden met phpinfo() horen daarom nooit op een bereikbaar systeem.