Защита от перебора паролей: SMTP, IMAP, веб — лимит по IP и по ящику #14

Merged
Liosha84 merged 1 commits from feat/login-guard into master 2026-11-06 18:44:16 +01:00
Owner

Что

Защита от перебора паролей перед выходом почты в интернет (mail.jambocheck.com, 75.6). Один счётчик LoginGuard на все проверки пароля: SMTP AUTH (25/587/465), IMAP LOGIN/AUTHENTICATE, вход в веб, смена пароля.

  • По IP: 10 неверных за 15 мин → блок 15 мин; каждый следующий блок того же IP вдвое дольше, до 24 ч.
  • По ящику: 20 неверных на один ящик с любых IP → ящик закрыт на 15 мин, кроме IP, с которых в него уже успешно входили (30 дней): владельца перебором не запереть.
  • Во время блока пароль не проверяется: SMTP 421 4.7.0 + обрыв, IMAP * BYE + NO [UNAVAILABLE], веб 429 + Retry-After.
  • Неверный пароль отвечается через 1 с; 3 неверных в одном соединении → обрыв (SMTP, IMAP).
  • «Нет ящика» и «неверный пароль» снаружи неразличимы (веб: и для отключённого ящика), оба считаются.
  • X-Forwarded-For принимается только от прокси в частной сети (своя замена forwardedHeaderTransformer); с публичного адреса — отбрасывается. Важно для docker-compose.public.yml: порт 8080 там опубликован и на публичном адресе ВМ.
  • Настройки LOGIN_GUARD_* (README), счётчики в памяти.

Попутно исправил IMAP: после * BYE терялся тегированный ответ (LOGOUT, обрыв после неудачных входов).

Проверка

  • ./gradlew :backend:test: 68 тестов, 0 упало (новые: LoginGuardTest, LoginGuardConfigTest, AuthenticationControllerTest, по одному в SmtpServerTest/ImapServerTest).
  • Живой запуск jar на временной Postgres: 4 неверных → 429 даже с верным паролем, ответ ~1,2 с на неверный; через прокси (X-Forwarded-For с localhost) — другой IP, 200; блок виден в логе.

Не сделано: лимит на регистрацию (на публичной почте она закрыта).

🤖 Generated with Claude Code

## Что Защита от перебора паролей перед выходом почты в интернет (`mail.jambocheck.com`, 75.6). Один счётчик `LoginGuard` на все проверки пароля: SMTP `AUTH` (25/587/465), IMAP `LOGIN`/`AUTHENTICATE`, вход в веб, смена пароля. - **По IP:** 10 неверных за 15 мин → блок 15 мин; каждый следующий блок того же IP вдвое дольше, до 24 ч. - **По ящику:** 20 неверных на один ящик с любых IP → ящик закрыт на 15 мин, **кроме IP, с которых в него уже успешно входили** (30 дней): владельца перебором не запереть. - Во время блока пароль не проверяется: SMTP `421 4.7.0` + обрыв, IMAP `* BYE` + `NO [UNAVAILABLE]`, веб `429` + `Retry-After`. - Неверный пароль отвечается через 1 с; 3 неверных в одном соединении → обрыв (SMTP, IMAP). - «Нет ящика» и «неверный пароль» снаружи неразличимы (веб: и для отключённого ящика), оба считаются. - `X-Forwarded-For` принимается только от прокси в частной сети (своя замена `forwardedHeaderTransformer`); с публичного адреса — отбрасывается. Важно для `docker-compose.public.yml`: порт 8080 там опубликован и на публичном адресе ВМ. - Настройки `LOGIN_GUARD_*` (README), счётчики в памяти. Попутно исправил IMAP: после `* BYE` терялся тегированный ответ (LOGOUT, обрыв после неудачных входов). ## Проверка - `./gradlew :backend:test`: 68 тестов, 0 упало (новые: `LoginGuardTest`, `LoginGuardConfigTest`, `AuthenticationControllerTest`, по одному в `SmtpServerTest`/`ImapServerTest`). - Живой запуск jar на временной Postgres: 4 неверных → `429` даже с верным паролем, ответ ~1,2 с на неверный; через прокси (`X-Forwarded-For` с localhost) — другой IP, `200`; блок виден в логе. Не сделано: лимит на регистрацию (на публичной почте она закрыта). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Liosha84 added 1 commit 2026-11-06 18:32:35 +01:00
Один LoginGuard на все проверки пароля (SMTP AUTH, IMAP LOGIN/AUTHENTICATE,
вход в веб, смена пароля): 10 неверных с IP за 15 мин → блок 15 мин, дальше
вдвое дольше до суток; 20 неверных на ящик с разных IP → ящик закрыт, кроме
IP, с которых в него уже входили. Во время блока пароль не проверяется
(SMTP 421, IMAP BYE + NO [UNAVAILABLE], веб 429 + Retry-After); неверный
пароль — ответ через 1 с; 3 неверных в соединении — обрыв.

X-Forwarded-For веб-входа — только от прокси в частной сети, иначе
заголовок отбрасывается (подменой IP лимит не обойти).

Попутно: IMAP терял тегированный ответ после "* BYE" (LOGOUT, обрыв после
неудачных входов) — takeUntil теперь после тегированной строки.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Liosha84 merged commit 0eb5348e4b into master 2026-11-06 18:44:16 +01:00
Sign in to join this conversation.