Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама

Senior: питання на співбесіді з теми «Інфраструктура й секрети»

Питання з реальних співбесід з відповідями: Laravel і PHP, бази даних, JavaScript і фронтенд, Git, Docker, API, безпека й архітектура. Тими самими темами, що й тести.

4 питання

WAF (Web Application Firewall) - фільтр HTTP-запитів перед застосунком. Хмарні WAF (Cloudflare, AWS WAF) працюють на краю мережі, ще до вашого сервера.

Що він добре робить:

  • поглинає DDoS-атаки мережевого рівня й об'ємні HTTP-флуди - ваш сервер їх не бачить;
  • блокує відомі шаблони атак (керовані правила для SQL-ін'єкцій, XSS, відомих CVE популярного ПЗ) - захист «на час», поки вразливість не виправлена в коді;
  • відсікає ботів і сканери: запити до /.env, /wp-admin, /phpmyadmin, підозрілі User-Agent, перевірки на кшталт Managed Challenge;
  • обмеження частоти на рівні краю - для логіну, пошуку, API;
  • геоблокування, списки IP, правила за ASN.

Чого WAF не робить:

  • не виправляє вразливостей у логіці застосунку: BOLA (чужий id в URL), відсутня авторизація, бізнес-логіка - для WAF це звичайні коректні запити;
  • не бачить усього: зашифровані дані в тілі, нестандартні формати, атаки, розтягнуті в часі;
  • обходиться кодуванням і варіаціями payload-ів - сигнатурний захист не буває повним.

WAF - додатковий шар, а не заміна безпечному коду.

Головна пастка - відкритий origin. Якщо IP-адреса сервера відома (історичні DNS-записи, піддомен без проксі, заголовки листів, сертифікати в журналах Certificate Transparency), зловмисник іде напряму на сервер, оминаючи WAF і захист від DDoS.

Як закрити origin:

  • фаєрвол приймає 80/443 лише з діапазонів IP Cloudflare (список публікується й змінюється - його треба оновлювати);
  • Authenticated Origin Pulls (mTLS між Cloudflare і сервером) - сервер перевіряє клієнтський сертифікат Cloudflare;
  • Cloudflare Tunnel - сервер узагалі не має відкритих вхідних портів, з'єднання ініціює агент з сервера;
  • нова IP-адреса після підключення проксі, якщо стара вже «засвітилася»;
  • усі піддомени, що ведуть на той самий сервер, - через проксі.

Справжній IP клієнта: за проксі Laravel бачить IP Cloudflare. Потрібно довіряти заголовкам лише від проксі (trustProxies з діапазонами Cloudflare) і брати CF-Connecting-IP - інакше ліміти частоти й журнали працюватимуть з неправильними адресами.

Докладніше в документації: Cloudflare: захист origin-сервера

Класичний підхід: у секретах CI лежать статичні ключі хмарного облікового запису (AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY) з правами на деплой.

Проблеми статичних ключів:

  • живуть роками - їх рідко ротують, бо це болісно;
  • витікають: у журналах збирання, через скомпрометовану залежність чи action у пайплайні, через неуважний echo, через форк з доступом до секретів;
  • працюють звідусіль - вкрадений ключ можна використати з будь-якого комп'ютера;
  • часто мають завеликі права («адміністратор, щоб точно запрацювало»).

OIDC (OpenID Connect) - без збережених секретів:

  1. на кожен запуск пайплайну CI-платформа (GitHub Actions, GitLab CI) видає короткоживучий підписаний токен з даними про запуск: репозиторій, гілка, середовище, workflow;
  2. хмарний провайдер (AWS, GCP, Azure) довіряє CI-платформі як провайдеру ідентичності й обмінює цей токен на тимчасові облікові дані на хвилини;
  3. довіра обмежена умовами: «лише репозиторій org/shop, лише гілка main, лише оточення production».
permissions:
  id-token: write   # дозволити запуску отримати OIDC-токен
  contents: read

steps:
  - uses: aws-actions/configure-aws-credentials@v4
    with:
      role-to-assume: arn:aws:iam::123456789012:role/deploy-shop
      aws-region: eu-central-1

Що це дає:

  • нема чого красти - у секретах CI немає довгоживучих ключів;
  • облікові дані живуть хвилини і прив'язані до конкретного запуску;
  • точні умови: пул-реквест з форку чи інша гілка роль не отримають.

Що важливо налаштувати правильно:

  • умови довіри (claims) мають бути вузькими - лише repo без гілки чи середовища дозволить будь-якій гілці (зокрема з необережного PR) деплоїти в продакшен;
  • права ролі - найменші: деплой конкретного сервісу, а не адміністратор облікового запису;
  • захист оточень у CI (обов'язкове затвердження для production).

Решта гігієни CI: закріплення сторонніх actions за хешем коміту (а не тегом), мінімальні permissions для GITHUB_TOKEN, обережність з pull_request_target, секрети не потрапляють у журнали.

Докладніше в документації: GitHub Actions: OpenID Connect

У хмарі застосунок отримує права не через ключі в .env, а через роль, прикріплену до сервера чи контейнера (IAM-роль інстансу, workload identity). Облікові дані видає сервіс метаданих за внутрішньою адресою 169.254.169.254.

Чому це зручно: немає статичних ключів, тимчасові облікові дані оновлюються автоматично.

Чому це небезпечно без захисту: будь-яка вразливість, що дозволяє змусити сервер зробити HTTP-запит (SSRF - «завантажити зображення за URL», генерація PDF з HTML, вебхук на адресу користувача), дає зловмиснику доступ до метаданих:

http://169.254.169.254/latest/meta-data/iam/security-credentials/app-role

і облікових даних ролі. Кілька відомих масштабних витоків даних почалися саме так.

Захист сервісу метаданих:

  • IMDSv2 обов'язковий (AWS): спершу PUT-запит за сесійним токеном, потім запити з заголовком токена. Типова SSRF дозволяє лише GET без власних заголовків - і токен отримати не може. Для нових інстансів IMDSv2 варто вимагати явно (HttpTokens=required);
  • hop limit = 1 - відповідь сервісу метаданих не проходить через додатковий мережевий «стрибок»: контейнер без host-мережі не дістане метадані хоста;
  • у Kubernetes - IRSA/workload identity замість ролі вузла: кожен под отримує власну роль.

Принцип найменших прав для ролі:

  • лише потрібні дії й ресурси: s3:PutObject і s3:GetObject на arn:aws:s3:::shop-uploads/*, а не s3:* на *;
  • окремі ролі для веб-застосунку, воркерів черг, задач бекапу - компрометація одного не відкриває решту;
  • жодних прав на IAM (створення користувачів і ролей) у застосунку;
  • умови: обмеження за VPC, тегами, мережею;
  • регулярний аудит невикористаних прав (IAM Access Analyzer та аналоги).

Захист від SSRF у коді лишається обов'язковим: перевірка URL і IP після розв'язання DNS, заборона приватних діапазонів і 169.254.0.0/16, вимкнені редиректи.

Виявлення: журнали хмарного провайдера (CloudTrail) з підозрілими діями від імені ролі застосунку (наприклад, виклики з невідомих IP) - сигнал до негайного реагування.

Докладніше в документації: AWS: налаштування сервісу метаданих (IMDS)

Ротація секретів потрібна регулярно (обмежити шкоду від непоміченого витоку) і негайно після підозри на компрометацію. Складність - не зламати роботу під час заміни.

Загальний принцип - період, коли діють обидва ключі:

  1. створити новий секрет;
  2. налаштувати систему приймати і старий, і новий;
  3. перевести всіх, хто використовує секрет, на новий;
  4. відкликати старий.

Так працює ротація ключів API партнерів, паролів до бази (два користувачі чи дві паролі в PostgreSQL), підписів вебхуків.

APP_KEY у Laravel шифрує cookie (зокрема сесійну), зашифровані атрибути моделей (каст encrypted), дані Crypt::encrypt(). Проста заміна ключа:

  • розлогінить усіх користувачів - старі cookie неможливо розшифрувати;
  • зламає зашифровані в базі дані - їх більше неможливо прочитати.

М'яка ротація через APP_PREVIOUS_KEYS:

APP_KEY="base64:новий..."
APP_PREVIOUS_KEYS="base64:старий..."
  • шифрування - завжди новим ключем;
  • розшифрування - спершу новим, потім по черзі попередніми.

Користувачі лишаються в системі, а сесії поступово перешифровуються новим ключем.

Але дані в базі самі не перешифруються. Значення, зашифровані старим ключем, лишаються такими, доки їх не перезапишуть. Перед тим як прибрати старий ключ із APP_PREVIOUS_KEYS, потрібна міграція даних: прочитати й зберегти кожне зашифроване значення (команда, що проходить по моделях частинами). Інакше видалення старого ключа - знову втрата даних.

Що ще залежить від APP_KEY: підписані URL (URL::signedRoute) - старі посилання стануть недійсними, якщо підпис не перевіряється попередніми ключами; знімки стану Livewire підписані ключем застосунку - відкриті сторінки отримають помилку при наступній дії.

Організаційно:

  • менеджер секретів (Vault, AWS Secrets Manager, Doppler, хмарні сховища) - центральне місце, журнал доступу, автоматична ротація для баз даних;
  • перелік, де використовується кожен секрет - без нього ротація перетворюється на пошук, що зламалося;
  • регламент реагування: хто і як ротує секрети при витоку, скільки часу це займає - відпрацьоване заздалегідь, а не вперше під час інциденту.

Докладніше в документації: Laravel: м'яка ротація ключів шифрування