Files
clinch/docs/security-todo.md
Dan Milne 9234904e47
Some checks failed
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled
Add security-todo and beta-checklists, and some security rake tasks
2026-01-01 13:06:54 +11:00

5.4 KiB

Security Status

Last Audit: 2025-12-31 Target Users: Self-hosters, small businesses

Beta Release Criteria: See beta-checklist.md for overall release readiness assessment.

This document demonstrates our proactive approach to security through systematic vulnerability tracking and remediation.


Summary

Phase Status Description
Phase 1-2 Complete Rate limiting, security headers, tests
Phase 3 Complete Critical fixes (token DoS, plaintext storage, fail-open)
Phase 4 Complete High priority (PKCE, WebAuthn, email re-auth, TOTP encryption)
Phase 5 🟡 In Progress Security enhancements
Phase 6 Optional Hardening & documentation

Outstanding Security Issues


MEDIUM - Account Lockout Mechanism

Files: app/controllers/sessions_controller.rb, app/models/user.rb Impact: Brute force attack mitigation

Implementation:

  • Add failed_login_attempts and locked_until columns to users
  • Progressive delays: 5 attempts → 5s, 10 → 1min, 15 → 15min, 20+ → 1hr
  • Admin notification on lockout
  • Configurable via MAX_LOGIN_ATTEMPTS ENV

MEDIUM - Per-Account Rate Limiting

Files: app/controllers/sessions_controller.rb, config/initializers/rack_attack.rb Impact: Distributed brute force prevention

Current: Global rate limiting only Needed: Add per-email rate limiting (10 failed attempts/email/hour)


LOW - WebAuthn Clone Detection Action

File: app/controllers/sessions_controller.rb:252-256 Impact: Cloned credential detection

Current: Logs warning on suspicious sign count Improvement: Block authentication, notify user/admin


Configuration Choices (Not Vulnerabilities)

These are policy decisions for self-hosters, not security bugs:

Item Default Notes
Session cookie domain Root domain Enables SSO across subdomains. Add SECURE_SUBDOMAIN_ISOLATION ENV to disable
CSP policy unsafe-inline, unsafe-eval Required for Stimulus/Turbo. Audit JS to remove if needed
Logout redirect validation Allows query params Per OAuth 2.0 spec. Document behavior
Invitation token lifetime 24 hours Add INVITATION_TOKEN_LIFETIME ENV for high-security deployments
Password minimum length 8 chars Add PASSWORD_MIN_LENGTH ENV, consider zxcvbn
Admin self-demotion check String comparison Minor - use .to_i for integer comparison

Completed Fixes

Phase 3 - Critical (December 2025)

1. Token Lookup DoS

  • Problem: O(n) BCrypt comparisons on token lookup
  • Solution: HMAC-based token prefix for O(1) indexed lookup
  • Files: token_prefixable.rb, token models, migration

2. Plaintext Token Storage

  • Problem: Access tokens stored in plaintext
  • Solution: Removed token column, use BCrypt digest only
  • Files: Migration, fixtures, tests

3. Forward Auth Fail-Open

  • Problem: Unmatched domains allowed by default
  • Solution: Changed to fail-closed (403 for unconfigured domains)
  • Files: forward_auth_controller.rb

Phase 4 - High Priority (December 2025)

4. PKCE Enforcement

  • Problem: PKCE was optional
  • Solution: Per-app PKCE with mandatory enforcement for public clients
  • Files: Application model, OIDC controller, migration

5. WebAuthn Info Disclosure

  • Problem: /webauthn/check leaked user_id and preferred_method
  • Solution: Minimal response, rate limiting (10/min), identical responses for non-existent users
  • Files: webauthn_controller.rb

6. OIDC State URL Encoding

  • Problem: State parameter not consistently URL-encoded
  • Solution: CGI.escape() on all redirect URLs
  • Files: oidc_controller.rb (4 locations)

7. Email Change Re-authentication

  • Problem: Email could be changed without password
  • Solution: Require current password for email changes
  • Files: profiles_controller.rb, view

12. TOTP Secret Encryption

  • Problem: TOTP secrets stored in plaintext
  • Solution: Rails encrypts with keys derived from SECRET_KEY_BASE
  • Files: user.rb, active_record_encryption.rb

13. WebAuthn Credential ID Enumeration

  • Problem: Global credential lookup allowed enumeration via 404 vs 403 responses
  • Solution: Scoped credential lookup to current user, identical responses
  • Files: webauthn_controller.rb, webauthn_credential_enumeration_test.rb

Security Strengths

  • Token security: HMAC prefix + BCrypt, no plaintext storage
  • Authorization codes: Pessimistic locking, single-use enforcement
  • Refresh tokens: Family tracking for rotation attack detection
  • Reserved claims: Validation prevents claim override attacks
  • Rate limiting: Applied on all authentication endpoints
  • Forward auth: Fail-closed by default
  • TOTP: AES-256-GCM encryption at rest
  • Email changes: Require password re-authentication
  • Credential isolation: Scoped lookups prevent enumeration attacks

Audit History

Date Event
2025-12-31 Credential ID enumeration fix (scoped lookups)
2025-12-31 Security review - 1 new issue found (credential enumeration)
2025-12-31 Phase 4 completed (PKCE, WebAuthn, email re-auth, TOTP)
2025-12-30 Phase 3 completed (token DoS, plaintext storage, fail-open)
2025-12-30 Comprehensive security audit - 18 issues identified
Earlier Phase 1-2 completed (rate limiting, headers, tests)