clusev/tests/Feature
boban f2cbf60c04 fix(security): harden brute-force, rate-limiting and auth-DoS (audit follow-up)
Adversarial audit of the auth surface confirmed several exploitable gaps. Every fix
auto-expires and never permanently locks the control plane (bare-IP recovery host and
the clusev:reset-admin CLI stay open).

Login:
- per-IP (20/min) + per-account (30/15min) buckets on top of the existing email+IP 5/min,
  so a distributed multi-IP brute-force of the single admin account is capped instead of
  scaling linearly with IPs. ip/acct counters decay on their own (a valid login doesn't
  reset a flood's budget).
- constant-time: the unknown-email branch now runs one bcrypt against a fixed DUMMY_HASH
  (evaluated before the `! $user ||` short-circuit), closing the account-enumeration
  timing oracle.

2FA:
- TOTP replay protection — verifyTotp uses verifyKeyNewer with a persisted last-used step
  (two_factor_last_used_step migration), so each 30s code is single-use. Passes 0 (never
  null) as the old step, since null makes verifyKeyNewer return bare `true` not the step.
- challenge + 2FA-proof reset gain an IP-independent per-account backstop, capping the
  distributed TOTP/backup brute-force surface.

Defense-in-depth / DoS:
- global throttle on /livewire/update (180/min per user-id-or-IP) via setUpdateRoute,
  keeping 'web' + the persistent EnsureSecurityOnboarded gate intact.
- reauth throttle (5/min per user) on password change/profile; SMTP test-send capped
  (3/10min per user) so the panel can't relay spam.
- SshClient gets a short, separate connect timeout (~5s) so a dead/tarpit host can't hold
  a worker across several sequential 15s reads (the observed 8.3s /livewire/update DoS).
- profile password policy raised to 12 chars + mixed case + numbers (was 10).

Tests: BruteForceHardeningTest covers TOTP replay, per-email + per-IP login caps,
identical unknown/known rejection, and reauth throttling. Full suite 187 green; Codex
clean; verified in a real browser (login/polls/Livewire updates unaffected by the throttle).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 17:59:45 +02:00
..
Modals feat(security): signed confirm-action tokens + airtight backup-code reveal 2026-06-15 18:35:33 +02:00
Settings feat(security): signed confirm-action tokens + airtight backup-code reveal 2026-06-15 18:35:33 +02:00
AuditRetentionTest.php feat(audit): configurable retention + scheduled clusev:prune-audit 2026-06-14 23:40:17 +02:00
BruteForceHardeningTest.php fix(security): harden brute-force, rate-limiting and auth-DoS (audit follow-up) 2026-06-17 17:59:45 +02:00
ButtonComponentTest.php feat(ui): uniform bordered button kit; retire ghost variants 2026-06-14 10:18:47 +02:00
ChallengeFactorAdaptTest.php fix(2fa): memoize challenge pending-user lookup; assert key-only view; tidy hint wording 2026-06-14 21:17:18 +02:00
CommandPaletteServerSearchTest.php fix(services): live journal poll + command-palette server search 2026-06-14 20:13:08 +02:00
ConfirmServerScopeTest.php feat(security): signed confirm-action tokens + airtight backup-code reveal 2026-06-15 18:35:33 +02:00
CreateServerTest.php feat(servers): verify SSH on create + start in "Initialisierung" 2026-06-14 10:26:15 +02:00
EmailResetTest.php fix(security): constant-time password-reset (flatten account-enumeration timing) 2026-06-15 19:16:30 +02:00
ExternalTlsModeTest.php feat(tls): external reverse-proxy mode core — no ACME + no forced HTTPS redirect (host check kept) 2026-06-14 22:31:04 +02:00
Fail2banBansModalTest.php feat(fail2ban): lazy-load Gesperrte IPs modal behind a skeleton 2026-06-15 03:05:35 +02:00
FirstFactorCodesTest.php fix(2fa): bound the backup-code reveal flag with a 10-minute TTL 2026-06-15 18:42:58 +02:00
FleetTestConnectionTest.php feat(ssh): add FleetService::testConnection credential probe 2026-06-14 10:24:42 +02:00
ForgotPasswordKeyOnlyTest.php feat(2fa): forgot-password is key-only safe + states the no-2FA recovery path 2026-06-14 21:26:59 +02:00
ForgotPasswordSmtpAwareTest.php fix(security): constant-time password-reset (flatten account-enumeration timing) 2026-06-15 19:16:30 +02:00
ForgotPasswordTest.php fix(auth): rotate remember_token on reset + atomic recovery-code use 2026-06-14 17:04:12 +02:00
ForgotPasswordTimingTest.php fix(security): flatten reset-timing residuals from Codex review 2026-06-15 19:43:40 +02:00
HardeningServiceTest.php feat(hardening): treat auto-updates as a neutral operator preference 2026-06-14 10:23:28 +02:00
MultiUserTest.php feat(security): signed confirm-action tokens + airtight backup-code reveal 2026-06-15 18:35:33 +02:00
OptionalOnboardingTest.php feat(2fa): onboarding no longer forces 2FA (only password rotation) 2026-06-14 20:44:12 +02:00
PersistentSecurityGateTest.php fix(security): register EnsureSecurityOnboarded as persistent Livewire middleware 2026-06-14 22:40:31 +02:00
RecoveryCodesModalTest.php feat(security): signed confirm-action tokens + airtight backup-code reveal 2026-06-15 18:35:33 +02:00
ResetAdminCommandTest.php fix(webauthn): counter zero only when stored is zero + CLI clears keys 2026-06-14 18:47:07 +02:00
RestartSentinelTest.php feat(system): auto-restart sentinel — one-click restart via host watcher (no docker socket) 2026-06-14 23:42:50 +02:00
ServerContextSyncTest.php fix(ui): set active server at mount only (race-free) 2026-06-14 19:43:24 +02:00
ServerShowPanelsTest.php fix(servers): keep read-error panels visible + pending header support 2026-06-14 10:41:04 +02:00
ServerShowSshHintTest.php feat(servers): hint that disabling password login leaves key-only access 2026-06-14 10:30:07 +02:00
ServicesJournalPollTest.php fix(services): cap live journal at 200 rows in a scrollable, auto-sticking box 2026-06-14 20:58:48 +02:00
SessionManagementTest.php feat(security): signed confirm-action tokens + airtight backup-code reveal 2026-06-15 18:35:33 +02:00
SettingsFactorManagementTest.php feat(security): signed confirm-action tokens + airtight backup-code reveal 2026-06-15 18:35:33 +02:00
SmtpConfigTest.php feat(mail): SMTP configuration in Settings (encrypted password, runtime override, test-send) 2026-06-14 23:42:19 +02:00
SshKeyProvisionModalTest.php fix(ssh): best-effort audit + exception-safe switch/verify and modal run(); lock serverId 2026-06-14 22:16:54 +02:00
SshKeyProvisionerTest.php fix(ssh): graceful failure if password-disable throws post-switch; document privateKey-on-failure contract 2026-06-14 22:04:56 +02:00
StatusComponentTest.php feat(ui): add "Initialisierung" (pending) server status 2026-06-14 10:20:32 +02:00
TlsModeToggleTest.php feat(security): signed confirm-action tokens + airtight backup-code reveal 2026-06-15 18:35:33 +02:00
TwoFactorChallengeRecoveryTest.php feat(auth): accept a 2FA backup code at the login challenge 2026-06-14 16:50:36 +02:00
TwoFactorWebauthnTest.php feat(webauthn): use a security key at the login challenge 2026-06-14 18:26:58 +02:00
UserFactorSemanticsTest.php feat(2fa): pluggable factor semantics on User (hasTotp, either-factor, resetIfNoFactor) 2026-06-14 20:38:36 +02:00
UserRecoveryCodesTest.php feat(auth): store 2FA recovery codes (encrypted) on users 2026-06-14 16:48:21 +02:00
ValidationMessagesTest.php feat(i18n): custom bilingual validation messages (DE + EN) 2026-06-14 15:45:02 +02:00
VerifyTotpTest.php fix(2fa): TwoFactorSetup back to auth layout; exception-safe User::verifyTotp at all call sites 2026-06-14 21:40:15 +02:00
WebauthnAvailableTest.php feat(webauthn): WebauthnService::available gate (domain + https) 2026-06-14 18:18:43 +02:00
WebauthnCredentialTest.php feat(webauthn): install web-auth/webauthn-lib + credential storage 2026-06-14 18:18:05 +02:00
WebauthnKeysTest.php feat(security): signed confirm-action tokens + airtight backup-code reveal 2026-06-15 18:35:33 +02:00
WebauthnOptionsTest.php feat(webauthn): registration/assertion options + ceremony verification 2026-06-14 18:24:10 +02:00