Skip to content

Security: authenticated password change is not wired into account lockout / rate limiting (SUF-04) #330

Description

@devondragon

Summary

The authenticated password-change endpoint verifies the current password but is not wired into the account-lockout / brute-force protections that the rest of the codebase uses. This is inconsistent with WebAuthn credential management, which verifies the same credential and does integrate lockout.

Severity: Medium (defense-in-depth / consistency; presupposes an authenticated session)

Affected code

  • src/main/java/com/digitalsanctuary/spring/user/api/UserAPI.javaupdatePassword (~336).
  • src/main/java/com/digitalsanctuary/spring/user/service/UserService.javacheckIfValidOldPassword (~787): a bare passwordEncoder.matches().
  • Contrast: src/main/java/com/digitalsanctuary/spring/user/api/WebAuthnManagementAPI.javarequireCurrentPasswordIfSet (~255) performs lock check + loginFailed() + loginSucceeded() around the same verification method.

Details

updatePasswordcheckIfValidOldPassword does not check whether the account is locked, does not record a failure via LoginAttemptService.loginFailed(), does not reset the counter on success, and applies no rate limit. LoginAttemptService is not injected into UserAPI on this path, and a wrong current-password guess does not emit a Spring Security authentication-failure event, so the configured lockout is never engaged.

Impact

Within an authenticated session, current-password guesses are not bounded by the configured lockout. bcrypt strength 12 slows each attempt but does not impose a finite attempt limit (CWE-307).

Recommended fix

  • Centralize critical-operation password reauthentication and reuse it across the password-change and WebAuthn-management paths.
  • Check lock state before hashing, increment the failure counter atomically on a wrong guess (return 401/423), and reset it on success.
  • Consider per-account/session/IP throttling around critical reauthentication.
  • Low-risk change: mirror the three LoginAttemptService calls already present in requireCurrentPasswordIfSet.

Identified during a code-first security assessment of the current main branch.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingsecurity

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions