Skip to content

security: harden auth — error disclosure, token blacklisting, rate limiting, timing#8

Merged
menottim merged 1 commit intomainfrom
security/fix-auth-hardening
Feb 27, 2026
Merged

security: harden auth — error disclosure, token blacklisting, rate limiting, timing#8
menottim merged 1 commit intomainfrom
security/fix-auth-hardening

Conversation

@menottim
Copy link
Copy Markdown
Owner

Summary

5 security fixes for the authentication subsystem:

  1. Error info disclosure (Critical) — Remove str(e) from 7 HTTPException detail fields in instances.py and search_queue.py. Internal error details (table names, connection strings) were being returned to clients.
  2. Validation error handler crash (Medium) — Fix TypeError: Object of type bytes is not JSON serializable when form-urlencoded input hits JSON endpoints. Now decodes bytes to strings.
  3. Access token not blacklisted on password change (High) — After password change, the current access token remained valid for up to 15 min. Now calls blacklist_access_token().
  4. disable_2fa missing rate limit and lockout (High) — Added @limiter.limit("3/minute") and increment_failed_login() on password failure.
  5. Timing side-channel on locked accounts (Medium) — Locked accounts returned in ~1ms vs ~2000ms for invalid passwords, enabling enumeration. Added dummy Argon2 hash to equalize timing.

Severity: Critical + High + Medium

Test plan

  • Verify error responses no longer contain internal exception text
  • Submit form-urlencoded data to a JSON endpoint — should get clean 422, not 500
  • After password change, verify old access token is rejected
  • Verify disable_2fa rate limiting works (4th attempt within 1 min should be blocked)
  • Verify locked account login timing matches invalid-password timing

Ref: docs/security-assessment-2026-02-27.md findings CRIT-3, HIGH-4, HIGH-7, MED-3, MED-5

🤖 Generated with Claude Code

- Remove exception details from HTTPException responses to prevent
  information disclosure (instances.py, search_queue.py)
- Fix validation_error_handler crash when request body contains bytes
  by sanitizing exc.errors() before JSON serialization (main.py)
- Blacklist current access token on password change for immediate
  session revocation (api/auth.py)
- Add rate limiting (3/min) and failed-login lockout to disable_2fa
  endpoint to prevent brute-force attacks (api/auth.py)
- Add dummy password verification on locked account check to prevent
  timing side-channel that reveals lock status (core/auth.py)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@menottim menottim merged commit c383a16 into main Feb 27, 2026
@menottim menottim deleted the security/fix-auth-hardening branch February 27, 2026 03:44
menottim added a commit that referenced this pull request Mar 1, 2026
Approved design for Features #8 and #9:
- Prowlarr as separate config (not instance type)
- Read indexer limits/stats from Prowlarr API, fall back to per-instance rate
- Season pack search with per-queue threshold (default 3)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
menottim added a commit that referenced this pull request Mar 5, 2026
…alse positives

Deleted leftover development scripts that logged secrets in plaintext:
- test_sqlcipher.py (alerts #1, #2)
- verify_phase1.py (alert #3)

Dismissed 8 false positive alerts with explanations:
- #4-7: py/incomplete-url-substring-sanitization in test assertions (not sanitization code)
- #8: py/stack-trace-exposure in health check (str(e) only in logger, response is generic)
- #9-10: py/weak-sensitive-data-hashing (SHA256 is pepper mixing before Argon2id, not the hash)
- #11: py/stack-trace-exposure in prowlarr (hardcoded error message, not str(e))
- #13: py/stack-trace-exposure in dashboard (Instance model field, not exception)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant