Базове правило: ключі не потрапляють у код і в Git. Вони живуть у змінних оточення, а код звертається до них через конфігурацію.
# .env - у .gitignore
STRIPE_SECRET=sk_live_...
NOVA_POSHTA_API_KEY=...
// config/services.php
'nova_poshta' => [
'key' => env('NOVA_POSHTA_API_KEY'),
'url' => env('NOVA_POSHTA_URL', 'https://api.novaposhta.ua/v2.0/json/'),
],
// у коді
Http::withToken(config('services.nova_poshta.key'));
Чому config(), а не env() у коді: після php artisan config:cache файл .env більше не читається, і env() поза конфігураційними файлами поверне null.
Що ще важливо:
.env.exampleмістить назви змінних без значень - новий розробник бачить, що потрібно налаштувати;- різні ключі для середовищ: тестові ключі пісочниці на локальній машині й staging, бойові - лише в продакшені;
- мінімальні права ключа: якщо провайдер дозволяє обмежити ключ (лише читання, певні IP, певні операції), - обмежувати;
- ротація: план заміни ключа без простою (новий ключ → деплой → відкликання старого) і обов'язкова заміна, якщо ключ міг витекти;
- не логувати секрети: заголовки
Authorization, тіла з ключами. У PHP 8.2+ атрибут#[\SensitiveParameter]приховує значення параметра в стеку помилки - Laravel використовує його, наприклад, уwithToken().
Зашифрований .env у репозиторії - вбудований механізм Laravel:
php artisan env:encrypt --env=production # створює .env.production.encrypted
php artisan env:decrypt --env=production --key=...
Зашифрований файл можна комітити, а ключ розшифрування передається лише в CI чи на сервер. Зручно для невеликих команд без окремого сховища секретів.
Для більших систем - менеджери секретів (Vault, AWS Secrets Manager, Doppler, секрети платформи деплою), де є аудит доступу й централізована ротація.
Токени користувачів до сторонніх сервісів (OAuth-доступ до їхніх Google чи GitHub) - це вже дані, а не конфігурація: зберігаються в базі зашифрованими (каст encrypted в Eloquent).
Ключ, що потрапив у Git, вважається скомпрометованим, навіть якщо коміт видалили: історія й форки лишаються. Тільки відкликання.