гортати · ПРОБІЛ далі
01 / 15
bash-app.com :: lecture // live

Захист цифрової
інфраструктури

Практичні налаштування для запобігання несанкціонованому доступу та витоку інформації.

Розбираємо на живій системі — реальні конфіги, реальні атаки, реальні захисти.

guest@bash-app.com:~$

01 / ідея

Захист — це шари, а не стіна

Один замок ламається. Defense in depth — кожен рівень підстраховує наступний. Зловмиснику треба пройти всі — а вам досить, щоб один витримав.

1Мережа та ізоляціяfirewall · Docker · least exposure
2Транспорт — TLS/HTTPScertbot · HSTS
3HTTP-заголовки безпекиCSP · X-Frame · nosniff
4Автентифікаціяargon2 · 2FA · rate-limit
5Секрети та шифрування.env · AES-256 · least privilege
6Захист від витоку та моніторингSPF/DKIM · логи · бекапи
02 / модель загроз

Від чого захищаємось?

Наведи/торкнись картки — приклад із реального життя.

Несанкціонований доступ

  • Брутфорс паролів
  • Credential stuffing
  • Обхід авторизації (IDOR)
  • Clickjacking

Витік інформації

  • Дамп бази даних
  • Секрети в репозиторії
  • Енумерація акаунтів
  • Перехоплення трафіку
навести на пункт…
03 / транспорт

TLSHTTPS: щоб трафік не читали

Перемкни — і подивись, що бачить зловмисник у публічному Wi-Fi.

$ Сертифікат за 1 команду

# Let's Encrypt, безкоштовно, авто-продовження
sudo certbot --nginx \
  -d bash-app.com -d www.bash-app.com

Примусовий HTTPS + HSTS

# nginx: весь :80 → :443
return 301 https://$host$request_uri;
# заголовок: наступні 2 роки лише HTTPS
Strict-Transport-Security: max-age=63072000;
  includeSubDomains; preload

Практика: TLS 1.2/1.3, редірект 80→443, HSTS (щоб не було навіть першого http-запиту), і Cloudflare у режимі Full (strict).

04 / HTTP-заголовки

Заголовки, що вмикають захист браузера

Це реальні заголовки відповіді bash-app.com. Клікни на будь-який — що він блокує.

Content-Security-Policydefault-src 'self'; object-src 'none'; frame-ancestors 'none'…
Білий список джерел скриптів/стилів/картинок. Головний захист від XSS: навіть якщо чужий скрипт потрапив на сторінку — браузер відмовиться його виконувати.
Strict-Transport-Securitymax-age=63072000; includeSubDomains; preload
Змушує браузер ходити лише по HTTPS — блокує downgrade і перехоплення на першому запиті.
X-Frame-OptionsDENY
Забороняє вбудовувати сайт у <iframe> — захист від clickjacking (див. наступний слайд).
X-Content-Type-Optionsnosniff
Забороняє браузеру «вгадувати» тип файлу — картинка не виконається як скрипт.
Referrer-Policystrict-origin-when-cross-origin
Не зливає повний URL (з токенами в параметрах) стороннім сайтам — захист від витоку через Referer.
Permissions-Policycamera=(), microphone=(), geolocation=()
Вимикає доступ до камери/мікрофона/геолокації для сторінки та вбудованого вмісту.
05 / демо · clickjacking

«Натисни на приз» — а насправді…

Зловмисник кладе прозорий iframe справжнього сайту поверх принади. Перетягни повзунок — і побачиш, що насправді під пальцем жертви.

👁 Сторінка-принада зловмисника:
🎁 Ви виграли iPhone!
натисніть, щоб забрати
🏦 bank.example / переказ
Підтвердити переказ 1000 ₴?
прозорість шару 0%

Як працює / як гасимо

  1. Атакуючий вбудовує справжній банк у прозорий <iframe>
  2. Малює зверху принаду; кнопки суміщені піксель-у-піксель
  3. Жертва «тисне приз» — а клік іде в невидимий банк → переказ
06 / демо · XSS

Ін'єкція скрипта та як її гасити

Уяви форму коментаря. Введи «зловмисний» рядок і подивись різницю.

Наївно: вставляємо як HTML

рядок потрапляє прямо в innerHTML

Правильно: екранування + CSP

React/шаблон екранує, CSP блокує виконання

Правило: ніколи не довіряй вводу. Дані = дані, не код. Екранування на виводі + CSP як друга лінія.

07 / демо · паролі

Паролі не зберігають. Їх хешують

Введи пароль — подивись, як його бачить база при різних підходах.

Відкритий текст

Дамп БД = усі паролі. Катастрофа.

~ Швидкий хеш (SHA-256)

Без солі + мільярди/сек на GPU → перебирається.

argon2id (сіль + повільно)

Унікальна сіль + навмисна повільність.

Два однакові паролі → різні хеші (сіль). Повільність — це фіча: 100 000 спроб/год замість мільярдів/сек.

08 / демо · 2FA

Другий фактор: пароль вкрали — і що?

TOTP-код (оновлюється кожні 30с)
000 000
як у Google Authenticator / Bitwarden

Навіть із вкраденим паролем зловмисник не увійде без коду, що живе 30 секунд лише у твоєму телефоні.

  • TOTP = час + секрет → 6 цифр (RFC 6238)
  • Backup-коди на випадок втрати телефона
  • Секрет 2FA у БД теж має бути захищений
09 / демо · брутфорс

Обмеження швидкості проти перебору

Стань «зловмисником»: тисни «спробувати ввійти» швидко. Дивись, коли система скаже «досить».

POST /api/login
спроб: 0 / 5

Ліміт 5 спроб на IP+email, далі — блок на 15 хв. Мільйон паролів перетворюється на роки.

  • Рахуємо по IP + email (не лише IP — за NAT)
  • Беремо реальний IP з X-Real-IP від nginx (не з підробного заголовка)
  • На рівні сервера — fail2ban банить IP у firewall
10 / секрети та шифрування

Секрети — поза кодом. Дані — зашифровані

🔑 Секрети поза git

# .gitignore
.env
# генеруємо, не вигадуємо:
openssl rand -hex 32   # AUTH_SECRET
Секрет у репозиторії = скомпрометований назавжди (git пам'ятає все).

🛡 Шифрування «в спокої» (AES-256-GCM)

Живий приклад: принцип найменших привілеїв

Сьогодні при налаштуванні пошти API-ключ спробував слати не зі свого домену — і сервіс відповів:
550 This API key is not authorized to send from send.bash-app.com
Це не баг, а фіча: ключ мав права лише на дозволене. Обмежуй кожен токен/ключ мінімумом — витік такого ключа не відкриває все.
11 / ізоляція

Назовні — лише те, що мусить

База даних не має «дивитися» в інтернет. Єдина точка входу — зворотний проксі.

🌐 Інтернет
→ лише 443
nginx :443
TLS, заголовки, rate-limit
app :3000
127.0.0.1 — не назовні
realtime :4000
127.0.0.1 — не назовні
postgres :5432
жодного порту назовні
  • Docker: БД лише у внутрішній мережі, порт не публікується
  • Контейнер працює від непривілейованого користувача, не root
  • Firewall: ufw — відкриті лише 22/80/443
  • SSH — тільки по ключу, вхід паролем вимкнено
  • Автооновлення безпеки: unattended-upgrades
12 / захист від витоку

Пошта, DNS та «тихі» відповіді

Підробка відправника (spoofing)

DNS-записи кажуть, хто має право слати від імені домену.

🤫 Анти-енумерація акаунтів

Реєстрація/скидання пароля відповідають однаково, існує акаунт чи ні:
If the address is available,
a confirmation email has been sent.
Плюс однаковий час відповіді — щоб не вгадали по затримці. Так атакуючий не збере базу email-ів.

І ніколи не логуй секрети/паролі/токени — логи теж витікають.

13 / інцидент · реальна історія

Коли щось таки пішло не так

Цей сайт нещодавно втратив домен — реєстратор заблокував акаунт. Доступ до klebold.xyz зник за годину.

💾 Що врятувало

  • Код — у git (повний бекап)
  • Дані — у томі pgdata
  • Документований HANDOFF

Відновлення

  • Новий домен bash-app.com
  • DNS → той самий сервер
  • Заміна домену в конфігах, новий TLS

Урок

  • Бекап 3-2-1
  • План відновлення заздалегідь
  • Не «всі яйця в кошику»

Безпека — це не лише «не пустити». Це ще й швидко відновитись, коли пустило.

14 / забери з собою

Практичний чеклист

Дякую! Питання?
Кожен пункт вище — реальне налаштування bash-app.com. Захист = шари + звичка не довіряти вводу + план на «якщо».