# Prawidłowa ochrona danych dostępowych i plików .env

> Ochrona pliku .env: właściwy webroot, blokady w serwerze WWW, wąskie uprawnienia, brak sekretów w Git i wymiana danych dostępowych po incydencie bezpieczeństwa.

Źródło: https://phpsos.de/pl/blog/env-dateien-schuetzen/  
Stan: 25.10.2026  
Dostawca: PHP SOS, Pomoc awaryjna PHP i bezpieczeństwo serwerów. Natychmiastowa pomoc przez WhatsApp: https://wa.me/491736494914?text=Dzie%C5%84%20dobry%2C%20PHP%20SOS%2C%20potrzebujemy%20pomocy%20z%20naszym%20serwerem%20PHP.%20Firma%3A%20%E2%80%A6%20Strona%20internetowa%3A%20%E2%80%A6

**Krótko:** Plik .env chronisz, nie umieszczając go w ogóle w katalogu dostępnym publicznie: webroot wskazuje na podkatalog taki jak `public/`, a konfiguracja leży poziom wyżej. Dodatkowo serwer WWW blokuje wszystkie pliki z kropką na początku, plik jest czytelny tylko dla właściciela i procesu PHP i nigdy nie trafia do repozytorium Git. Po włamaniu wszystkie zawarte w nim dane dostępowe uznaje się za znane i wymienia.

## Plik z kluczami do domu

Nowoczesne projekty PHP chętnie przechowują swoje sekrety w pliku o nazwie `.env`: hasło do bazy danych, dostęp do serwera pocztowego, klucze API dla usług płatności i wysyłki, klucz, którym aplikacja podpisuje ciasteczka. Frameworki takie jak Laravel i Symfony stosują ten wzorzec, wiele własnych rozwiązań go przejęło.

Pomysł jest dobry: konfiguracja i kod są oddzielone, a każde środowisko dostaje własne wartości. Niebezpiecznie robi się, gdy plik trafi do niewłaściwego katalogu. Jeśli leży w webroot, a serwer WWW go nie blokuje, wystarczy jedno wywołanie w przeglądarce, aby odczytać wszystkie dostępy naraz.

Dochodzą warianty o innych nazwach: `.env.local`, `.env.production`, `config.local.php` lub pliki z danymi dostępowymi do usług przechowywania. To, co tu opisano dla `.env`, dotyczy wszystkich plików, w których znajdują się sekrety.

Takie pliki są nieustannie wyszukiwane przez zautomatyzowane skanery. To nie specjalność pojedynczych atakujących, lecz tło internetu, podobnie jak masowe skanowanie w poszukiwaniu niezałatanych wersji PHP.

## Gdzie powinien leżeć plik .env

Najskuteczniejszy środek jest strukturalny: serwer WWW może wydawać tylko ten katalog, który ma być naprawdę publiczny. W Laravelu i Symfony jest to katalog `public/`. Wszystko inne, czyli kod, konfiguracja, logi i `.env`, leży wyżej i przez HTTP w ogóle nie jest adresowalne.

```text
/srv/www/shop/
├── .env              ← niedostępny
├── config/
├── src/
├── vendor/
└── public/           ← jedyny webroot
    ├── index.php
    └── assets/
```

W praktyce webroot mimo to często wskazuje na sam katalog projektu, na przykład dlatego, że panel hostingu tak narzuca albo że przy przeprowadzce nie dostosowano ścieżki. Sprawdź więc w konfiguracji serwera WWW lub w panelu, na co wskazuje `root` albo `DocumentRoot`.

## Ochrona pliku .env: blokada w serwerze WWW jako druga linia

Jeśli nie da się zmienić webroot albo chcesz dodatkowego zabezpieczenia, zablokuj ukryte pliki w serwerze WWW. Katalog `.well-known` musi przy tym pozostać dostępny, ponieważ służy między innymi do wystawiania certyfikatów.

```nginx
# Zablokować wszystkie pliki i katalogi z kropką na początku, z wyjątkiem .well-known
location ~ /\.(?!well-known/) {
    deny all;
    return 404;
}
```

```apache
# Zablokować pliki z kropką na początku
<FilesMatch "^\.">
    Require all denied
</FilesMatch>
```

Odpowiedź 404 zamiast 403 nie zdradza, że plik istnieje. To, czy blokada działa, sprawdzasz z zewnątrz:

```bash
# Oczekiwane: 403 lub 404, nigdy 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
```

Dwa ostatnie wiersze nie są przypadkowe: kopie takie jak `.env.bak` lub `.env.old` powstają przy pracach serwisowych i często są zapominane. Więcej o takich pozostałościach znajduje się w artykule [Pliki kopii zapasowych w webroot](https://phpsos.de/pl/blog/backup-dateien-im-webroot/).

## Uprawnienia, Git i postępowanie z kopiami

Także poza webroot obowiązuje zasada najmniejszych uprawnień. Sprawdzony podział:

1. **Właścicielem** jest użytkownik wdrożeniowy, a nie proces PHP.
2. **Grupą** jest grupa puli PHP-FPM, która może tylko czytać; uprawnienia `640`.
3. **Brak dostępu do zapisu** dla procesu PHP. Aplikacja nie musi móc zmieniać własnych danych dostępowych.
4. **Brak prawdziwych wartości w repozytorium:** Wprowadzany jest tylko `.env.example` z wartościami zastępczymi, prawdziwy plik znajduje się w `.gitignore`.
5. **Oddzielne wartości dla każdego środowiska:** Programowanie, staging i produkcja używają różnych dostępów, aby wyciek w środowisku testowym nie otworzył produkcji.

```bash
chown deploy:shop-php /srv/www/shop/.env
chmod 640 /srv/www/shop/.env
# Sprawdzić, czy plik kiedykolwiek został zatwierdzony
git log --all --oneline -- .env
```

Zwróć też uwagę na strony błędów. Frameworki w trybie debugowania częściowo wyświetlają zmienne środowiskowe i wartości konfiguracji; w systemie produkcyjnym ten tryb musi być wyłączony. Sprawdzaj to po każdym wdrożeniu, ponieważ błędna wartość w samym `.env` może go ponownie włączyć.

Jeśli ostatnie polecenie zwraca trafienia, dane dostępowe znajdują się w historii Git i uchodzą za ujawnione, nawet jeśli pliku dziś nie ma. Dlaczego jest to szczególnie delikatne, gdy katalog `.git` leży na serwerze, opisuje artykuł [Publiczny katalog .git: niedoceniany wyciek danych](https://phpsos.de/pl/blog/git-ordner-oeffentlich/). Podstawy uprawnień wyjaśnia poradnik [Uprawnienia do plików na serwerze WWW](https://phpsos.de/pl/ratgeber/dateirechte-webserver/).

> **Ważne:**
> Pliki testowe z `phpinfo()` wyświetlają zmienne środowiskowe. Kto przekazuje sekrety jako zmienne środowiskowe, zdradza je przez zapomniany plik testowy. Tło opisano w artykule [Pliki phpinfo(): dlaczego muszą zniknąć](https://phpsos.de/pl/blog/phpinfo-dateien-entfernen/).

## Po włamaniu: wymiana danych dostępowych

Żadna blokada nie chroni przed atakującym, który już wykonuje kod na serwerze. Proces PHP musi móc czytać swoją konfigurację, więc wstrzyknięty kod w tym samym procesie też może. Po PHP2Shell obowiązuje więc: jeśli nie można wykluczyć włamania, wszystkie dane dostępowe na serwerze należy traktować jako znane.

Uporządkowana wymiana obejmuje hasła do baz danych, dostępy do poczty i SMTP, klucze API usług zewnętrznych, klucze podpisujące aplikacji oraz dostępy do usług przechowywania. Kolejność i moment powinny być zaplanowane: kto najpierw wymienia, a potem czyści serwer, może od razu przekazać atakującemu nowe wartości.

Szkody wynikające z wycieku ograniczasz już wcześniej, sprawiając, że każdy sekret może tylko tyle, ile potrzeba. Użytkownik bazy danych strony nie potrzebuje uprawnień do innych baz. Klucze API usług zewnętrznych można u wielu dostawców ograniczyć do pojedynczych funkcji lub adresów nadawców, na przykład zakładanie przesyłek, ale bez zlecania wypłat. A każda aplikacja otrzymuje własne dostępy, zamiast aby kilka stron dzieliło to samo konto pocztowe lub ten sam klucz. Wtedy wyciek dotyczy jasno wyodrębnionego obszaru, a wymiana pozostaje przejrzysta.

W środowiskach kontenerowych plik `.env` nie należy ani do obrazu, ani do publicznie dostępnego rejestru. Wyklucz go przez `.dockerignore` i przekazuj sekrety dopiero w czasie działania za pomocą mechanizmów platformy.

Na dłuższą metę warto zwrócić uwagę na centralne magazyny sekretów, jakie zaleca OWASP: sekrety w jednym miejscu, z dziennikiem dostępu, regularną wymianą i możliwie krótką ważnością.

## Jak konkretnie pomaga PHP SOS?

- Sprawdzamy z zewnątrz, czy pliki konfiguracyjne, kopie i ukryte pliki Państwa stron są dostępne do pobrania.
- Przestawiamy webroot, blokady serwera WWW i uprawnienia do plików tak, aby sekrety nie były już osiągalne przez HTTP.
- Sprawdzamy repozytoria i wdrożenia pod kątem zatwierdzonych danych dostępowych.
- Po incydencie planujemy i wspieramy wymianę wszystkich dotkniętych dostępów we właściwej kolejności.

Te prace są częścią naszego [utwardzania serwerów PHP](https://phpsos.de/pl/leistungen/server-haertung/).


## Częste pytania

**Czy plik .env w webroot jest automatycznie publiczny?**  
Nie automatycznie, ale często. To, czy można go pobrać, zależy od konfiguracji serwera WWW. Wiele konfiguracji domyślnych chroni tylko pliki .htaccess, a nie inne pliki z kropką na początku. Test za pomocą curl wyjaśnia sprawę.

**Czy zmienne środowiskowe są bezpieczniejsze niż plik .env?**  
Nie z zasady. OWASP zwraca uwagę, że zmienne środowiskowe są czytelne dla wszystkich procesów użytkownika i mogą pojawiać się w logach lub zrzutach pamięci. Unikają jednak pliku w webroot. Lepsze są centralne magazyny sekretów, jeśli środowisko je obsługuje.

**Co zrobić, jeśli plik .env był dostępny do pobrania?**  
Wszystkie zawarte w nim dane dostępowe uznać za znane i wymienić: baza danych, poczta, API, usługi płatnicze, klucze aplikacji. Następnie sprawdzić w logach dostępu, czy plik został pobrany, a w przypadku danych osobowych włączyć inspektora ochrony danych.

**Czy plik .env należy do repozytorium Git?**  
Nie. Do repozytorium należy co najwyżej szablon bez prawdziwych wartości, na przykład .env.example. Prawdziwy plik jest wykluczany przez .gitignore i udostępniany na serwerze oddzielnie.

**Czy phpinfo() może zdradzić dane dostępowe?**  
Tak. phpinfo() wyświetla między innymi zmienne środowiskowe i zmienne serwera. Jeśli znajdują się tam sekrety, zostaną wyświetlone. Pliki testowe z phpinfo() nie mogą więc nigdy trafić na dostępny system.

## Źródła

- [OWASP Secrets Management Cheat Sheet (ściągawka o zarządzaniu sekretami)](https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html)
- [Dokumentacja Nginx](https://nginx.org/en/docs/)
- [Serwer HTTP Apache: dokumentacja](https://httpd.apache.org/docs/)
