Безопасность¶
Безопасность встроена, а не «прикручена сверху». На этой странице собраны меры, которые реализованы в коде.
Аутентификация и сессии¶
| Мера | Подробности |
|---|---|
| eID — единственный вход | Единственный интерактивный способ входа — eID (QR / App2App / push по регистрационному номеру). Поверхности паролей нет вообще |
| JWT access + refresh | refresh-токены ротируются; защищены claim kind |
| Список запрещённых при выходе | При выходе jti access-токена помещается в Redis на остаток TTL; middleware проверяет его на каждом запросе |
| Сертификат гражданина (PKI) | По завершении входа возвращается сертификат гражданина (DER), разбирается через crypto/x509; серийный номер, срок действия и издатель сохраняются |
| Привязка Google | Только привязка — по стабильному столбцу subject |
Отсутствие паролей — осознанное решение
Поскольку парольного потока нет, такие меры, как HIBP / bcrypt / проверка утёкших паролей, неприменимы. Устаревшие usecase с паролями и OTP остались в дереве кода, но недоступны ни по одному маршруту. Если парольный путь когда-нибудь снова откроют, подключите проверку HIBP до релиза.
Слой данных¶
- Только параметризованные запросы (pgx) — без конкатенации строк, без ORM.
- Row-Level Security —
ENABLEиFORCEна каждой пользовательской таблице:users,organizations,organization_memberships, гражданские таблицыgov_*иuser_integrations. Политики опираются на GUCapp.user_id/app.user_role, задаваемые в каждой транзакции черезSET LOCAL. - Нет личности ⇒ ноль строк (fail-closed), что защищает от случайного раскрытия.
Проверка RLS при старте
При запуске приложение проверяет собственную роль в базе. В продакшене роль
суперпользователя или с BYPASSRLS прерывает старт — иначе RLS
молча не применялся бы. В разработке выводится только предупреждение.
Каждой новой пользовательской таблице нужны собственные политики.
Секреты и шифрование¶
| Что | Как |
|---|---|
| OAuth-токены сторонних сервисов | Запечатываются AES-256-GCM перед сохранением (INTEGRATION_ENC_KEY) |
| Идентификаторы токенов / сессий | crypto/rand с отбраковкой значений, чтобы избежать смещения по модулю |
| MFA суперадмина (TOTP) | Тоже шифруется ключом INTEGRATION_ENC_KEY |
Никогда не меняйте INTEGRATION_ENC_KEY на месте
Смена уже используемого ключа сломает все ранее зашифрованные значения. Скрипт развёртывания записывает его один раз, только если он отсутствует (идемпотентно).
Веб- и сетевой слой¶
- Заголовки безопасности — CSP
default-src 'none', HSTS (прод),nosniff,X-Frame-Options: DENY, Referrer-Policy, Permissions-Policy, COOP/CORP/COEP. - CORS — строгий список разрешённых origin; никогда
*вместе с credentials. - Ограничение размера тела — общий лимит плюс 4 КиБ на
/auth. - Таймаут на запрос — обычно 30 с; для
/ai/*— 50 с (TTS/STT в Gemini обычно занимает 10–20 с и не укладывается в 30 с). - Полные таймауты сервера —
ReadHeader10 с,Read30 с,Write70 с,Idle120 с,MaxHeaderBytes16 КиБ (защита от slowloris и огромных заголовков). - Ограничение запросов —
/auth~5/мин,/ai/*~20/мин, анонимный чат на главной/public/ai/chat~6/мин — на IP. - Permissions-Policy —
camera=(), microphone=(self), geolocation=(). Микрофон разрешён только для этого origin (голосовой чат вызываетgetUserMedia); приmicrophone=()браузер отклоняет вызов сразу, даже не спрашивая разрешения.
Фронтенд (модель BFF)¶
Браузер общается только с маршрутами /api/* своего origin. Токены живут в
куках httpOnly и никогда не попадают в клиентский JS. Каждый изменяющий
вызов несёт заголовок x-gerege-csrf, который сервер проверяет через
checkOrigin — двойная защита от CSRF.
Журнал аудита¶
С хеш-цепочкой и только на добавление:
Записи сериализуются через pg_advisory_xact_lock; VerifyChain делает
подделку заметной. Чтение — только для админа.
Авторизация (RBAC)¶
Динамический каталог ролей и разрешений на четырёх уровнях: суперадмин →
админ → менеджер → пользователь. Маршруты защищены middleware
RequirePermission / RequireAdmin. Суперадмин — единственная роль, управляющая
пользователями-администраторами, и он никогда не создаётся через API — только
через базу данных или окружение.
Эксплуатационное усиление¶
В продакшене /metrics и /swagger/doc.json закрыты bearer-токеном (сравнение
за постоянное время, 404 при промахе). Логи — структурированные Zap с
идентификатором запроса, секреты никогда не логируются.
Дорожная карта ASVS¶
| Уровень | Статус |
|---|---|
| L1 | ✅ HTTPS + HSTS, беспарольный вход, параметризованные запросы, заголовки, строгий CORS, валидация ввода, структурированные логи, секреты не в репозитории. ⏳ сканирование контейнеров / govulncheck |
| L2 | ✅ ограничение запросов, ротация refresh, привязка устройства eID (устойчиво к фишингу), таймауты запросов, шифрование интеграционных токенов, аудит с хеш-цепочкой. ⏳ WAF, централизованный SIEM, тест восстановления из бэкапа, план реагирования |
| L3 | ◻ шифрование PII на уровне полей (KMS), mTLS, провенанс SLSA L3, внешний пентест — вне рамок шаблона |
Известные пробелы¶
- Интерактивный Swagger UI — отдаётся только сырая спецификация по адресу
/swagger/doc.json(загрузите её в Swagger Editor или Postman). - Полная матрица мер — в
backend/docs/SECURITY.md.
Сообщение об уязвимости
Пожалуйста, не открывайте публичный issue. Следуйте процессу из SECURITY.md.