Junior: питання на співбесіді з теми «Інфраструктура й секрети»
Питання з реальних співбесід з відповідями: Laravel і PHP, бази даних, JavaScript і фронтенд, Git, Docker, API, безпека й архітектура. Тими самими темами, що й тести.
5 питань
Секрети - паролі до бази, APP_KEY, ключі API, токени платіжних систем - не повинні жити в коді. У Laravel їх зберігають у файлі .env, який:
- ніколи не комітиться - він уже є в
.gitignoreстандартного проєкту; - має шаблон
.env.exampleу репозиторії - з назвами змінних, але без справжніх значень; - на сервері доступний лише користувачу, від якого працює застосунок (
chmod 600), і лежить поза публічним каталогом (public/).
У коді секрети читаються лише через конфігурацію: config('services.stripe.secret'), а env() - тільки у файлах config/*.php. Після php artisan config:cache виклики env() поза конфігурацією повертають null.
Якщо .env потрапив у Git (навіть на хвилину, навіть у приватний репозиторій):
- вважати всі секрети з файлу скомпрометованими і змінити їх - згенерувати нові ключі API, паролі до бази, новий токен бота. Це головний крок;
- видалити файл з репозиторію й додати в
.gitignore; - за потреби переписати історію (
git filter-repo) - але це вже другорядне: копія могла лишитися у форках, клонах, кешах CI, на GitHub у вигляді доступних за хешем комітів.
Видалення файлу без ротації ключів нічого не захищає - автоматичні сканери знаходять секрети в публічних репозиторіях за лічені хвилини.
Профілактика:
- сканування секретів перед пушем: GitHub secret scanning з push protection,
gitleaksчиtrufflehogу pre-commit або CI; - окремі ключі для кожного оточення (локальне, staging, продакшен) - витік локальних ключів не зачіпає продакшен;
- ключі з найменшими правами - токен, якому потрібно лише читати, не повинен уміти писати;
- для командної роботи - зашифрований
.env(php artisan env:encrypt) чи менеджер секретів, а не пересилання файлу в месенджері.
Інструменти, що допомагають під час розробки, на продакшені стають готовою розвідкою для зловмисника: вони показують запити до бази, змінні оточення, сесії, токени й внутрішню структуру застосунку. Сканери шукають їхні адреси автоматично.
Що перевірити перед запуском:
| Що | Чим небезпечне відкрите | Як закрити |
|---|---|---|
Laravel Telescope (/telescope) |
запити з тілами, SQL, винятки, вміст сесій і кешу | лише --dev залежність або gate viewTelescope з переліком адмінів |
Laravel Horizon (/horizon) |
дані завдань у черзі (часто з email чи id), можливість повторити чи видалити завдання | Gate::define('viewHorizon', ...) - за замовчуванням на не-локальному середовищі доступу немає ні в кого, але ворота часто «відкривають» для зручності |
| Debugbar | SQL з параметрами, сесія, конфігурація на кожній сторінці | лише в require-dev, APP_DEBUG=false |
Сторінка винятку (APP_DEBUG=true) |
стек викликів, шляхи, фрагменти коду, змінні оточення | APP_DEBUG=false на продакшені |
phpinfo(), info.php |
версії, модулі, змінні середовища, іноді ключі | не тримати такі файли в public/ |
| Pulse, Log Viewer та інші панелі | метрики, логи з персональними даними | авторизація через Gate |
.env, .git, storage/, composer.json |
секрети й вихідний код | корінь вебсервера - лише public/ |
| адмінка й Filament | повний доступ до даних | canAccessPanel(), двофакторна автентифікація, бажано обмеження за IP чи VPN |
Принципи:
- dev-пакети - у
require-dev, а на продакшеніcomposer install --no-dev: якщо пакета немає, його неможливо випадково відкрити; - ворота (
Gate) замість «таємної адреси» - змінений шлях/telescope-x7k2не захист, його знаходять перебором і з логів; - перевірка ззовні: після деплою відкрити ці адреси без входу (чи прогнати сканер) - очікується 404 чи 403, а не сторінка інструменту;
- автоматичний тест: Pest-тест, що гість отримує 403 на
/horizonі/telescope, ловить випадкову зміну воріт ще до продакшену.
Додатковий шар - WAF чи правило на рівні проксі, що блокує типові службові шляхи (/phpinfo, /.env, /_ignition, /telescope) для всіх, крім адміністраторів. Але це доповнення до закритих інструментів, а не заміна.
Застосунок часто підключається до бази під обліковим записом з усіма правами - root у MySQL чи postgres у PostgreSQL. Поки все працює, різниці не видно. Різниця з'являється, коли щось іде не так.
Що дає зловмиснику всемогутній користувач:
- через SQL-ін'єкцію - не лише читання даних, а
DROP DATABASE, читання й запис файлів сервера (LOAD DATA,COPY ... TO PROGRAMу PostgreSQL для суперкористувача), створення нових користувачів; - через вкрадений
.env- повний контроль над усіма базами на сервері, а не лише над даними застосунку.
Принцип найменших прав - кожен обліковий запис має лише те, що йому справді потрібно:
| Користувач | Права |
|---|---|
| застосунок | SELECT, INSERT, UPDATE, DELETE на свою базу |
| міграції (деплой) | + CREATE, ALTER, DROP, INDEX на свою базу |
| аналітика, звіти | лише SELECT, бажано на репліці |
| бекап | читання й блокування, без зміни даних |
-- PostgreSQL
CREATE ROLE app LOGIN PASSWORD '...';
GRANT CONNECT ON DATABASE shop TO app;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO app;
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO app;
Інші правила:
- база не доступна з інтернету: слухає лише внутрішню мережу чи
localhost, порт закритий фаєрволом; - окремі користувачі для кожного застосунку на спільному сервері бази - витік одного не відкриває інші;
- з'єднання з TLS, якщо база на іншому сервері;
- довгі випадкові паролі, різні для кожного оточення;
- журнал підключень і помилок автентифікації.
Практичний компроміс у Laravel: міграції часто запускають тим самим користувачем, що й застосунок. Окреме підключення для міграцій (--database=migrations) з ширшими правами - кращий варіант для продакшену.
У Laravel-проєкті кореневий каталог сайту (document root) - лише public/. Там лежать index.php і зібрані ассети. Усе інше - код, .env, storage/, vendor/, .git/ - має бути поза доступом вебсервера.
Типова помилка - document root вказує на корінь проєкту:
root /var/www/shop; # неправильно
root /var/www/shop/public; # правильно
Тоді за прямими посиланнями доступні:
/.env- паролі до бази,APP_KEY, ключі API. Сканери перевіряють цей шлях на мільйонах сайтів щодня;/.git/- з каталогу.gitінструменти на кшталтgit-dumperвідновлюють увесь вихідний код і історію комітів, включно з колись видаленими секретами;/storage/logs/laravel.log- стеки помилок, інколи персональні дані;/composer.json,composer.lock- точні версії залежностей для пошуку відомих вразливостей;- резервні копії й дампи (
backup.sql,.env.bak,index.php~), залишені в публічних каталогах.
Як захиститися:
- document root =
public/- головне правило; - у Nginx - заборона прихованих файлів, крім
.well-known(потрібен для сертифікатів Let's Encrypt іsecurity.txt):
location ~ /\.(?!well-known).* {
deny all;
}
- не класти в
public/нічого, крім того, що має бути публічним. Файли користувачів - черезstorage:linkлише для публічного диска, приватні - через контролер з перевіркою прав; - не деплоїти
.gitна сервер, якщо він не потрібен (збірка артефакту в CI); - регулярна перевірка ззовні:
curl -I https://example.com/.envмає повертати404чи403. Такі перевірки варто додати в моніторинг.
WAF-правила (наприклад, у Cloudflare), що блокують запити до .env, .git, wp-admin, phpmyadmin, - корисний додатковий рубіж, але не заміна правильному document root.
Бекап захищає від багатьох сценаріїв: помилкового DELETE, збою диска, зламу, програм-вимагачів, що шифрують дані й вимагають викуп. Але лише якщо його зроблено правильно.
Правило 3-2-1:
- 3 копії даних (основна + дві резервні);
- на 2 різних носіях чи типах сховищ;
- 1 копія поза основною інфраструктурою - інший провайдер, регіон, обліковий запис.
Захист від вимагачів і зловмисників:
- зловмисник, що отримав доступ до сервера, видалить і бекапи, якщо сервер має права на їх видалення. Тому:
- сервер має лише право дописувати нові копії, але не видаляти чи перезаписувати старі;
- незмінні копії - Object Lock в S3-сумісних сховищах (режим compliance), версіонування бакетів;
- окремий обліковий запис для бекапів, до якого немає доступу з продакшену;
- офлайн- чи ізольовані копії - CISA рекомендує тримати хоча б одну копію поза мережею.
Шифрування: бекапи містять усі персональні дані й секрети. Вони мають бути зашифровані, а ключ - зберігатися окремо від бекапів (і не лише на тому самому сервері).
Що бекапити:
- базу даних (дамп чи фізична копія + журнали для відновлення на момент у часі);
- файли користувачів (
storage/app, бакет S3); - конфігурацію й секрети (зашифрованими) - без них відновлений застосунок не запуститься.
Найважливіше - перевірка відновлення. Бекап, який жодного разу не відновлювали, - лише надія. Регулярне (автоматизоване) відновлення в окреме оточення з перевіркою:
- дамп розгортається без помилок;
- ключові таблиці мають очікувану кількість записів;
- застосунок запускається на відновлених даних.
Так заодно вимірюється реальний час відновлення - і він часто неприємно дивує.
Моніторинг: сповіщення, якщо бекап не створився чи його розмір різко змінився (і раптове зменшення - ознака проблеми).
Докладніше в документації: CISA: посібник з протидії програмам-вимагачам