← Auth / Security

Защита и инфраструктура

auth / security · workzone
Входящий HTTPS-запрос
1
TLS-терминация
Запрос принимается только по шифрованному каналу — иначе отклонение.
HTTPS only TLS 1.3 · 1.2 min HTTP → redirect Сертификаты Let's Encrypt / корп. CA
Edge-hardening → лимиты ресурса на Nginx, до приложения: client_max_body_size, client_*_timeout, limit_conn, large_client_header_buffers. Раньше и грубее лимитов ③⑤: по сырому соединению, не по опознанному клиенту.
2
Security-заголовки · Nginx
Каждый ответ дополняется защитными заголовками, которые задают браузеру правила.
HSTS max-age=1y; includeSubDomains CSP default-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self' X-Content-Type-Options nosniff X-Frame-Options DENY Cross-Origin-Opener-Policy same-origin Cross-Origin-Resource-Policy same-origin Referrer-Policy strict-origin-when-cross-origin Permissions-Policy Cache-Control no-store на чувствительных Clear-Site-Data на logout Токен-роуты no-referrer Server · X-Powered-By off
3
CORS · граница доверия
FastAPI middleware определяет, какому origin доверять.
Allow-Origin whitelist из env Allow-Credentials true Allow-Methods GET/POST/PUT/DELETE/PATCH Allow-Headers Authorization, Content-Type, X-Request-Id · X-CSRF-Token v2 Expose-Headers Retry-After, X-RateLimit-Remaining, X-Request-Id Preflight Max-Age 3600 API rate limiting per-user, по ролям · Redis >
4
CSRF-проверка → Refresh cookie
Мутирующий запрос должен подтвердить, что отправлен со своего origin.
SameSite=Strict на refresh cookie Origin / Referer на каждой мутации
SSO v2 → переход на SameSite=Lax + CSRF-токен для cookie-authenticated endpoints (/refresh).
Защиту самого входа через провайдера (state / PKCE / nonce) держит карточка SSO / OIDC.
5
Рубеж защиты от перебора → Контракт ошибок → Admin Panel → Cache & Workers
Логин — все три слоя; per-account часть (слои 2–3) накрывает и проверку текущего пароля на смене пароля. Перебор сдерживается, не блокируя владельца.
Слой 1 Rate limit по IP — 20 попыток / 15 мин на IP. За reverse proxy IP берётся из X-Forwarded-For (только от trusted proxies).
Слой 2 Задержка per-account — первые 2 неудачи без задержки (запас на опечатки), с 3-й экспоненциально 1с→2с→4с→8с (предел 30с), на стороне сервера.
Слой 3 Alert Owner/Admin после 10 неудач per-account — уведомление.
Единый ответ об ошибке Сброс счётчиков успех / TTL 15 мин Лог попыток pre-auth, IP + email Состояние в Redis brute:ip · brute:account
Задержка, а не висящее соединение → за порогом отдаём 429 + Retry-After, а не держим запрос открытым: иначе сами висящие соединения становятся вектором нагрузки.
Несуществующий email → dummy argon2 verify (constant-time): ответ занимает столько же, сколько для реального аккаунта.
MFA → свой лимит, отдельный от пароля: 5 неудачных вводов TOTP или recovery-кода сжигают промежуточный токен входа — дальше заново с пароля. Перебор 6-значного кода через MFA-шаг так не пройдёт.
Код привязки мессенджера → свой лимит на chat_id: 5 неудачных вводов сжигают попытку — за новым кодом в веб. Публичный webhook бота — под rate-limit по IP/источнику; истёкший и неверный код неотличимы (единый 410 LINK_EXPIRED). Бот открыт всем, поэтому рубеж жёстче, чем у закрытого воркспейсом Slack.
Пределы рубежа и отложенные меры
v2 Платформа по инвайтам, без публичной регистрации → главная угроза — credential stuffing по конкретным аккаунтам; задержка + MFA закрывают её без трения. CAPTCHA вернётся адаптивной (только по риск-сигналам), если эксплуатационные логи покажут необходимость.

Распределённый перебор (ботнет, password spraying — один пароль по многим аккаунтам) per-IP лимит не ловит: с каждого адреса всего по паре попыток. На раннем этапе — принятый предел; глобальный детектор аномалий ждёт v2 по эксплуатационным логам.

Тот же единый ответ — на восстановлении пароля: «если аккаунт существует, письмо отправлено», независимо от того, есть ли он.
6
Сквозной рубеж: всё, что генерируется, сравнивается и хранится на диске.
Нужен ли серверу оригинал секрета обратно?
Нет Хэш · необратимо
argon2id медленный
  • Пароли
  • Recovery-коды
memory 19 MB iterations 2 parallelism 1
SHA-256 быстрый
  • Refresh-токены
  • Ссылки сброса
  • API-ключи
  • CSRF v2
Да Шифрование · обратимо
AES-256-GCM по ключу
  • smtp_settings.password_enc
  • sources.credential_enc
  • sources.webhook_secret_enc
  • ai_providers.api_key_enc
  • tools.credential_enc
  • notification_channels.url_enc
  • notification_channels.secret_enc
  • mfa_secret v2
CSPRNG токенов secrets.token_urlsafe() ≥32B Constant-time hmac.compare_digest() Хэш токенов в БД SHA-256 Подпись JWT HMAC-SHA256
Encryption at rest v2
mfa_secretAES-256-GCM (секрет нужен обратимо: сервер считает по нему TOTP). Ключ из env/vault, key rotation — несколько версий (key ID в данных). Recovery codes не шифруются, а хэшируются (argon2id), как пароли: одноразовые, сервер их только сверяет.
7
На выходе каждое значимое событие фиксируется навсегда.
Что логируем значимые события, не каждый запрос Формат реляционная схема + JSONB Хранение append-only PostgreSQL · только Owner
Своя таблица, не библиотека · и второй слой defense-in-depth
Своя таблица. Тонкая обёртка audit_log(actor, action, target, result, ip, ua) — фиксирует намерение актора, а не diff строк (этим заняты готовые row-версионеры). Application-дефолт: OWASP ASVS V7, NIST SP 800-92.

Единая точка записи. Обёртку зовут централизованно — middleware на изменяющих ручках плюс доменные события, не россыпь ручных вызовов: забытый вызов — дыра в аудите без всякого сигнала.

Hash-chaining v2. Каждая запись хранит хеш предыдущей — лог становится tamper-evident: задним числом строку не отредактировать и не удалить незаметно, цепочка хешей разойдётся. Поверх append-only прав, для высокого уровня доверия.
Что в аудит не попадает → пароли, токены и секреты — никогда; чувствительное поле — идентификатором, не содержимым (old→new только для неконфиденциального: роль, статус). Лог неудаляемый и навсегда — PII, попавшее сюда, уже не вычистить.
Запись аудита → вход пишется асинхронно: отказ лога не роняет сам вход. Изменения прав — запись обязательна и в своей транзакции, чтобы result=failure не потерялся при откате основной операции.
Срок хранения и архивация v2 → конфиг audit_retention_days, по умолчанию «бесконечно». Сам джоб чистки и выгрузку в холодное хранилище проектируем вместе с hash-chaining — удаление должно дружить с неизменностью, а срок задаёт compliance (SOC 2 / ISO 27001).
Least-privilege роль БД → приложение ходит в Postgres ролью без DDL и без чужих схем: даже пробитый инъекцией запрос заперт в своих таблицах. Defense-in-depth под слоем данных, не только параметризация.
Прямой доступ к БД в обход приложения → ловит pgaudit v2: SQL-операции пишутся в самой БД, application-аудит этого не видит. Форензика для compliance (SOC 2 / ISO 27001).
✓ Запрос прошёл защиту → обработка прошедший все рубежи запрос допущен к бизнес-логике; его след — в аудит-логе