Безопасность редко «ломается» из‑за экзотического zero-day. Чаще — из‑за .env в коммите, общего sudo и образа latest без скана. Ниже — то, что я реально держу в процессе, а не в презентации 🔐
Секреты: не в репозитории и не в чате #
Правило: секрет, который хоть раз попал в git/чат/скрин — считайте скомпрометированным. Ротация, не «ну мы удалили коммит».
Где жить секретам:
| Место | Когда |
|---|---|
Vaultwarden / rbw | личные токены, TOTP, API keys |
| Infisical / Vault | shared env для стеков и CI |
| CI secrets (Forgejo/GitHub) | только то, что нужно пайплайну |
/etc/.../*.env с 640 | runtime на сервере, вне docroot |
Что не делать:
# плохо
echo "TOKEN=sk-live-...." >> .env
git add .env && git commit -m "config"
# лучше — шаблон без значений
cp .env.example .env # .env в .gitignorePre-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 .Привычки в коде #
- Валидация входа — всё, что пришло из HTTP/CLI/очереди, не «доверенное».
- Параметризованные запросы — никаких
f"SELECT … {user_id}". - Ошибки наружу — без stack trace и путей
/home/…в ответе API. - Сессии/cookie —
HttpOnly,Secure,SameSiteза reverse-proxy сX-Forwarded-Proto. - 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:
- Ограничить blast radius (отозвать ключ, закрыть порт, stop container).
- Зафиксировать факты (время, логи, что именно утекло) — без паники в публичный чат.
- Ротация всех связанных секретов (не «только тот один»).
- Постмортем на полстраницы: как повторить защиту (хук, CI, права).
Что сознательно не входит в этот пост #
Compliance-театр, ISO-чеклисты и «купите WAF». Здесь — инженерный минимум, после которого можно углубляться. Если чего-то из списка нет в вашем пайплайне — начните с gitleaks + pin образов + секреты вне git: это закрывает половину реальных историй 🛡️