pytest — 147 passed (было 131; +16 тестов на F1/F2/F4/F5/F6/F7).
alembic upgrade head на свежей БД → 0013, колонка token_version создаётся; путь идемпотентен (как 0003/0011). Данные dev/prod не трогались.
Тесты покрывают: throttle админ-входа (429), отзыв токена после logout и смены пароля + выживание другого устройства, сброс пароля админом рвёт сессии игрока, лимит на аккаунт при ротации IP, throttle регистрации, fail-fast конфига в проде, закрытие openapi.json/docs в проде.
Требует живой проверки на test-клоне (backend-тестами не покрывается): F3 (header_up + --forwarded-allow-ips — перебор с подменой XFF), F6 security-заголовки и включение CSP (не сломать SPA / Telegram-виджет / SSE). Caddyfile деплоится на VPS вручную.
Закрываемые векторы: онлайн-перебор пароля админа (F1), бессрочность украденного токена (F2), снятие лимита и порча аудита подменой IP (F3), компрометация через дефолтные секреты (F4), распределённый перебор (F5).
Инварианты: «dev-код не в проде» сохранён (правки в общих модулях, dev-guard'ы не тронуты); контракт API не изменился → фронт не трогали (gen:api не требуется).
Замечания
Q1 (профиль/история любого игрока доступны любому вошедшему) — отдельной задачей не заводили (вероятно by design); в этот PR не входит.
F5: лимит на аккаунт намеренно щедрый (вектор lockout-DoS); вынос счётчиков в Redis — при переходе на несколько воркеров (сейчас --workers 1), вне этого прохода.
Закрывает находки пен-теста #25 (F1–F7). Одна ветка, по коммиту на находку.
## Что сделано
| Находка | Задача | Severity | Правка |
|---|---|---|---|
| F1 | #56 | High | Вход админа проходит через throttle (пара IP+логин, IP, аккаунт); неудачи — в аудит |
| F2 | #57 | High | Отзыв JWT: `token_version` (смена/сброс пароля) + jti-denylist (logout); миграция `0013` |
| F3 | #58 | High | Caddy перезаписывает `X-Forwarded-For` реальным пиром; uvicorn `--forwarded-allow-ips` сужен |
| F4 | #59 | High | `production` не стартует с дефолтными/слабыми `SECRET_KEY`/`ADMIN_PASSWORD` |
| F5 | #60 | Medium | IP-независимый лимит на аккаунт; заметка про рестарт/воркеры/Redis |
| F6 | #61 | Low | Схема API закрыта в проде; security-заголовки на edge (CSP подготовлена, выкл.) |
| F7 | #62 | Low | Throttle регистрации по IP |
## Что проверено
- `pytest` — **147 passed** (было 131; +16 тестов на F1/F2/F4/F5/F6/F7).
- `alembic upgrade head` на свежей БД → `0013`, колонка `token_version` создаётся; путь идемпотентен (как `0003`/`0011`). Данные dev/prod не трогались.
- Тесты покрывают: throttle админ-входа (429), отзыв токена после logout и смены пароля + выживание другого устройства, сброс пароля админом рвёт сессии игрока, лимит на аккаунт при ротации IP, throttle регистрации, fail-fast конфига в проде, закрытие `openapi.json`/docs в проде.
- **Требует живой проверки на test-клоне** (backend-тестами не покрывается): F3 (`header_up` + `--forwarded-allow-ips` — перебор с подменой XFF), F6 security-заголовки и включение CSP (не сломать SPA / Telegram-виджет / SSE). `Caddyfile` деплоится на VPS вручную.
## Модель угроз (кратко; полная — в отчёте #25)
- **Активы:** учётные записи игроков и администратора, JWT-сессии (`fs_session` 7 дней, `fs_admin` 8 ч), аудит.
- **Нарушители:** неаутентифицированный внешний, вошедший игрок, владелец утёкшего токена, сетевой наблюдатель.
- **Закрываемые векторы:** онлайн-перебор пароля админа (F1), бессрочность украденного токена (F2), снятие лимита и порча аудита подменой IP (F3), компрометация через дефолтные секреты (F4), распределённый перебор (F5).
- **Инварианты:** «dev-код не в проде» сохранён (правки в общих модулях, dev-guard'ы не тронуты); контракт API не изменился → фронт не трогали (`gen:api` не требуется).
## Замечания
- **Q1** (профиль/история любого игрока доступны любому вошедшему) — отдельной задачей не заводили (вероятно by design); в этот PR не входит.
- F5: лимит на аккаунт намеренно щедрый (вектор lockout-DoS); вынос счётчиков в Redis — при переходе на несколько воркеров (сейчас `--workers 1`), вне этого прохода.
Closes #56
Closes #57
Closes #58
Closes #59
Closes #60
Closes #61
Closes #62
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01XfTsytzT6TojfmprRDKiV6
logout отзывает предъявленный токен по jti (in-memory denylist до exp);
смена и сброс пароля инкрементят users.token_version (claim ver в JWT,
сверка в auth/deps) — все прежние сессии отзываются. Своё устройство при
смене пароля остаётся в сессии (перевыдача cookie). Миграция 0013. #57
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XfTsytzT6TojfmprRDKiV6
admin_login проходит через LoginThrottle (пара IP+логин, IP и сам аккаунт),
как вход игрока; исчерпание лимита -> 429. Неудачные попытки пишутся в аудит
(без пароля). Пароль админа — прямой путь к полному контролю, лимиты строже. #56
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XfTsytzT6TojfmprRDKiV6
Вход игрока и админа получают лимит login-user/admin-login-user, не зависящий
от IP: ротация X-Forwarded-For (#58) больше не снимает защиту полностью.
Успешный вход сбрасывает счётчики аккаунта. В docstring ratelimit — про сброс
при рестарте и необходимость внешнего стора при нескольких воркерах. #60
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XfTsytzT6TojfmprRDKiV6
throttle_register ограничивает частоту POST /api/auth/register с одного IP
через тот же LoginThrottle (считаются все попытки). Enumeration ников через
409 не закрываем — ники и так публичны в топе (отмечено в отчёте). #62
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XfTsytzT6TojfmprRDKiV6
model_validator в Settings: при APP_ENV=production приложение не стартует, если
SECRET_KEY дефолтный/короче 32 символов или (при включённом бутстрапе) ADMIN_PASSWORD
дефолтный/пустой. dev/test не затронуты — там дефолты остаются нормой. #59
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XfTsytzT6TojfmprRDKiV6
main.py: openapi.json/docs/redoc отдаются только в dev/test (нужны для gen:api),
в production отключены. Caddyfile (edge): HSTS, X-Content-Type-Options, X-Frame-Options,
Referrer-Policy, Permissions-Policy, скрыт Server; CSP подготовлена, но выключена до
проверки на test-клоне (строгая политика ломает SPA/Telegram-виджет/SSE). #61
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XfTsytzT6TojfmprRDKiV6
entrypoint.sh: --forwarded-allow-ips сужен с "*" до loopback + приватных сетей
compose (переопределяемо FORWARDED_ALLOW_IPS) — uvicorn сканирует XFF справа и
берёт реальный адрес, подставленное клиентом левое значение игнорируется.
Caddyfile: reverse_proxy перезаписывает X-Forwarded-For реальным пиром
(header_up {remote_host}) вместо добавления. Итог — достоверный IP для throttle
и аудита. Инфра-часть проверяется на test-клоне. #58
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XfTsytzT6TojfmprRDKiV6
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Закрывает находки пен-теста #25 (F1–F7). Одна ветка, по коммиту на находку.
Что сделано
token_version(смена/сброс пароля) + jti-denylist (logout); миграция0013X-Forwarded-Forреальным пиром; uvicorn--forwarded-allow-ipsсуженproductionне стартует с дефолтными/слабымиSECRET_KEY/ADMIN_PASSWORDЧто проверено
pytest— 147 passed (было 131; +16 тестов на F1/F2/F4/F5/F6/F7).alembic upgrade headна свежей БД →0013, колонкаtoken_versionсоздаётся; путь идемпотентен (как0003/0011). Данные dev/prod не трогались.openapi.json/docs в проде.header_up+--forwarded-allow-ips— перебор с подменой XFF), F6 security-заголовки и включение CSP (не сломать SPA / Telegram-виджет / SSE).Caddyfileдеплоится на VPS вручную.Модель угроз (кратко; полная — в отчёте #25)
fs_session7 дней,fs_admin8 ч), аудит.gen:apiне требуется).Замечания
--workers 1), вне этого прохода.Closes #56
Closes #57
Closes #58
Closes #59
Closes #60
Closes #61
Closes #62
🤖 Generated with Claude Code
https://claude.ai/code/session_01XfTsytzT6TojfmprRDKiV6
entrypoint.sh: --forwarded-allow-ips сужен с "*" до loopback + приватных сетей compose (переопределяемо FORWARDED_ALLOW_IPS) — uvicorn сканирует XFF справа и берёт реальный адрес, подставленное клиентом левое значение игнорируется. Caddyfile: reverse_proxy перезаписывает X-Forwarded-For реальным пиром (header_up {remote_host}) вместо добавления. Итог — достоверный IP для throttle и аудита. Инфра-часть проверяется на test-клоне. #58 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XfTsytzT6TojfmprRDKiV6