Безопасность редко «ломается» из‑за экзотического zero-day. Чаще — из‑за .env в коммите, общего sudo и образа latest без скана. Ниже — то, что я реально держу в процессе, а не в презентации 🔐

Секреты: не в репозитории и не в чате #

Правило: секрет, который хоть раз попал в git/чат/скрин — считайте скомпрометированным. Ротация, не «ну мы удалили коммит».

Где жить секретам:

МестоКогда
Vaultwarden / rbwличные токены, TOTP, API keys
Infisical / Vaultshared env для стеков и CI
CI secrets (Forgejo/GitHub)только то, что нужно пайплайну
/etc/.../*.env с 640runtime на сервере, вне docroot

Что не делать:

# плохо
echo "TOKEN=sk-live-...." >> .env
git add .env && git commit -m "config"

# лучше — шаблон без значений
cp .env.example .env   # .env в .gitignore

Pre-commit ловит глупости до push:

# пример: gitleaks
brew install gitleaks
gitleaks protect --staged
# или хук pre-commit

Если токен уже уехал: revoke → issue new → grep по истории (gitleaks detect, git log -p -S 'sk-'), обновить все потребители.

SAST / DAST без «галочки в аудите» #

SAST (код до запуска):

  • semgrep / gosec / bandit — по языку стека;
  • dependency scan: npm audit, pip-audit, trivy fs .;
  • в CI — fail на high/critical, не «warning forever».

DAST (уже крутится):

  • OWASP ZAP baseline на staging, не на prod с боевыми данными;
  • не путать «зелёный ZAP» с «безопасно»: это дымовой тест поверхности.

Минимальный CI-шаг:

# идея, не догма
- name: secrets
  run: gitleaks detect --no-git -v
- name: deps
  run: trivy fs --severity HIGH,CRITICAL .

Привычки в коде #

  1. Валидация входа — всё, что пришло из HTTP/CLI/очереди, не «доверенное».
  2. Параметризованные запросы — никаких f"SELECT … {user_id}".
  3. Ошибки наружу — без stack trace и путей /home/… в ответе API.
  4. Сессии/cookieHttpOnly, Secure, SameSite за reverse-proxy с X-Forwarded-Proto.
  5. Least privilege — отдельный пользователь/пул php-fpm/service account, не «всё под root».

Инфраструктура рядом с кодом #

Образы:

# pin digest, не latest
image: nginx:1.26.3@sha256:…
trivy image myapp:1.2.3

Доступ:

  • SSH keys only, PermitRootLogin no, AllowUsers явно;
  • deploy-ключ с restrict / без sudo;
  • firewall: default drop, только нужные порты с нужных source.

Логи: реальный IP за прокси (real_ip / X-Forwarded-For с доверенного hop), иначе fail2ban банит NPM, а не сканер.

Когда уже инцидент #

Короткий playbook:

  1. Ограничить blast radius (отозвать ключ, закрыть порт, stop container).
  2. Зафиксировать факты (время, логи, что именно утекло) — без паники в публичный чат.
  3. Ротация всех связанных секретов (не «только тот один»).
  4. Постмортем на полстраницы: как повторить защиту (хук, CI, права).

Что сознательно не входит в этот пост #

Compliance-театр, ISO-чеклисты и «купите WAF». Здесь — инженерный минимум, после которого можно углубляться. Если чего-то из списка нет в вашем пайплайне — начните с gitleaks + pin образов + секреты вне git: это закрывает половину реальных историй 🛡️