Sekrety poza repozytorium — .env i config.php
🎯 Po tej lekcji będziesz przechowywać hasła i klucze poza repozytorium gitowym, używać .gitignore do ich blokowania i wczytywać konfigurację bezpiecznie do kodu PHP.
W Module 8 widziałeś połączenie PDO z wpisanym na stałe hasłem do bazy danych:
<?php
// ❌ ANTY-PRZYKŁAD — tak NIE rób!
$pdo = new PDO('mysql:host=localhost;dbname=sklep', 'root', 'TajneHaslo123');
To hasło teraz żyje w historii gita na zawsze. Każdy kto sklonuje repo — kolega, rekruter, atakujący — je zobaczy. Ta lekcja nauczy cię właściwego podejścia.
Problem: sekrety w kodzie
Dlaczego historia gita jest niebezpieczna
Git zapamiętuje każdy commit. Nawet jeśli usuniesz hasło i zrobisz nowy commit, nadal można je znaleźć:
git log --all --full-history # lista commitów
git show <stary-commit>:db.php # pokaż plik sprzed usunięcia hasła
Jeśli repo jest publiczne na GitHubie, boty skanujące wyciekłe sekrety znajdą je w kilka sekund od git push. Atakujący automatycznie rotuje w tym czasie hasła i klucze API.
Co jest sekretem?
- Hasło do bazy danych (
DB_PASS) - Klucze API (Stripe, Mailchimp, SendGrid…)
- Klucz szyfrowania sesji / JWT secret
- Dane SMTP (login + hasło poczty)
- Wszelkie tokeny dostępu do zewnętrznych usług
Rozwiązanie 1: plik .env i .gitignore
Najpopularniejsze podejście — plik .env w katalogu głównym projektu (nigdy w public/):
# .env — NIGDY nie commituj tego pliku!
DB_HOST=localhost
DB_NAME=zadania_db
DB_USER=appuser
DB_PASS=TajneHasloDoZmiany
Zablokuj go w .gitignore:
# .gitignore
.env
*.log
git status i upewnij się, że .env NIE pojawia się na liście. Jeśli był już śledzony wcześniej, musisz go odśledzić przez git rm --cached .env.Plik .env.example
Commituj do repo pusty szablon — tylko nazwy zmiennych, bez wartości:
# .env.example — commituj! To dokumentacja dla nowych developerów.
DB_HOST=localhost
DB_NAME=
DB_USER=
DB_PASS=
APP_ENV=development
Nowy developer kopiuje: cp .env.example .env i wpisuje swoje dane.
Wczytywanie konfiguracji — bez zewnętrznych bibliotek
PHP ma wbudowaną funkcję parse_ini_file, która świetnie nadaje się do wczytania prostego pliku .env:
<?php
declare(strict_types=1);
// config.php — plik wczytywany raz na starcie aplikacji
$envSciezka = dirname(__DIR__) . '/.env'; // katalog nad public/
if (!file_exists($envSciezka)) {
throw new RuntimeException(
'Brak pliku .env. Skopiuj .env.example i uzupełnij dane.'
);
}
$config = parse_ini_file($envSciezka);
if ($config === false) {
throw new RuntimeException('Nie można odczytać pliku .env.');
}
// Użycie:
// $config['DB_HOST'], $config['DB_NAME'], ...
Połączenie PDO z konfiguracją z .env
<?php
declare(strict_types=1);
require_once 'config.php'; // ładuje $config
$pdo = new PDO(
dsn: 'mysql:host=' . $config['DB_HOST'] . ';dbname=' . $config['DB_NAME'] . ';charset=utf8mb4',
username: $config['DB_USER'],
password: $config['DB_PASS'],
options: [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
],
);
Hasło zostało w .env, nie w kodzie. Kod możesz commitować.
Rozwiązanie 2: config.php poza public/
Inny popularny wzorzec — osobny plik konfiguracyjny poza katalogiem serwera webowego:
projekt/
├── public/ ← dostępne z internetu (DocumentRoot serwera)
│ └── index.php
├── src/
├── config.php ← poza public/, nie dostępne przez HTTP
└── .env ← poza public/, w .gitignore
<?php
// config.php — poza public/, NIE commitujemy tego pliku
declare(strict_types=1);
return [
'db_host' => 'localhost',
'db_name' => 'zadania_db',
'db_user' => 'appuser',
'db_pass' => 'TajneHaslo', // hasło tutaj, nie w repo
];
<?php
// public/index.php
declare(strict_types=1);
$config = require dirname(__DIR__) . '/config.php'; // poza public/
// $config['db_pass'] jest teraz dostępne
Dodaj do .gitignore:
config.php
.env
Biblioteka phpdotenv (Moduł 7)
W Module 7 zainstalowałeś Composera. Popularna biblioteka vlucas/phpdotenv automatyzuje wczytywanie .env:
composer require vlucas/phpdotenv
<?php
declare(strict_types=1);
$dotenv = Dotenv\Dotenv::createImmutable(dirname(__DIR__));
$dotenv->load();
// Zmienne dostępne przez $_ENV lub getenv():
$dbPass = $_ENV['DB_PASS'];
phpdotenv oferuje też walidację — sprawdza, czy wymagane zmienne są ustawione:
$dotenv->required(['DB_HOST', 'DB_NAME', 'DB_USER', 'DB_PASS']);
Lista kontrolna bezpieczeństwa sekretów
- [ ]
.envjest w.gitignore - [ ]
.env.examplejest w repo (bez wartości) - [ ] Hasła i klucze API nie ma w żadnym pliku PHP w repo
- [ ]
git log --allnie zawiera haseł w historii - [ ] Plik konfiguracyjny leży poza katalogiem
public/ - [ ] Serwer produkcyjny używa zmiennych środowiskowych lub secrets managera
Podsumowanie
| Złe nawyki | Dobre nawyki |
|---|---|
| Hasło w kodzie PHP | Hasło w .env lub config.php poza repo |
| Żaden .gitignore | .env zawsze w .gitignore |
| Hardkodowane klucze API | Klucze przez $_ENV['API_KEY'] lub getenv() |
| Jeden plik konfiguracji dla dev i prod | Osobne .env dla dev i prod |
W kolejnych dwóch lekcjach zbudujesz projekt końcowy — aplikację „Moje zadania" z kontami użytkowników, łącząc wszystko czego się nauczyłeś.
Sprawdź się
Dlaczego nie wolno trzymać hasła do bazy danych bezpośrednio w kodzie PHP w repozytorium?
Git przechowuje całą historię. Nawet jeśli usuniesz hasło w kolejnym commicie, nadal jest dostępne przez git log lub git show. Publiczne repozytoria na GitHub są indeksowane przez boty wyszukujące wyciekłe sekrety w sekundach od push.
Jak sprawdzić że plik .env jest zablokowany przed trafieniem do repozytorium git?
.gitignore wpisany wzorzec '.env' sprawi że git ignoruje ten plik przy git add. Uruchom git status i upewnij się że .env nie pojawia się na liście śledzonych plików. Dopiero wtedy jesteś bezpieczny.
Czym jest plik .env.example (albo .env.dist)?
.env.example zawiera tylko nazwy zmiennych (bez wartości) lub fikcyjne wartości przykładowe. Commitujemy go do repo jako dokumentację. Każdy developer kopiuje go jako .env i wpisuje własne hasła. Nigdy nie commit .env z prawdziwymi danymi.
Ćwiczenie
Stwórz plik .env z przykładową konfiguracją bazy danych (DB_HOST, DB_NAME, DB_USER, DB_PASS). Dodaj .env do .gitignore. Napisz plik config.php, który wczytuje .env przez parse_ini_file() i zwraca tablicę konfiguracji. Wyświetl nazwę bazy, ukrywając hasło (pokaż tylko '***').
Pokaż rozwiązanie
# Plik .env:
DB_HOST=localhost
DB_NAME=moja_baza
DB_USER=root
DB_PASS=TajneHaslo123
# Plik .gitignore:
.env
<?php
// config.php
declare(strict_types=1);
$sciezkaEnv = dirname(__DIR__) . '/.env';
if (!file_exists($sciezkaEnv)) {
throw new RuntimeException('Brak pliku .env — skopiuj .env.example i uzupełnij dane.');
}
$config = parse_ini_file($sciezkaEnv);
if ($config === false) {
throw new RuntimeException('Nie można odczytać pliku .env.');
}
// Użycie:
echo 'Baza: ' . $config['DB_NAME'] . PHP_EOL; // wynik: Baza: moja_baza
echo 'Hasło: ***' . PHP_EOL; // wynik: Hasło: ***