5.1.0 — Security Hardening (SUF-01 through SUF-06)
Security hardening from the SUF review series (SUF-01 through SUF-06). Most changes are backward compatible.
This is a minor release: it adds new public API — the StepUpService SPI and new user.security.* properties (appUrl/trustedHosts/requireCanonicalAppUrl, allowInitialPasswordSetWithoutStepUp) — alongside the security hardening. No source- or binary-incompatible API changes were made. The one runtime behavior change (POST /user/setPassword disabled by default) is a fail-closed security default, reversible with a single documented property.
⚠️ Behavior change:POST /user/setPasswordis now disabled by default (SUF-02). See Behavior changes below andMIGRATION.md.
Installation
Gradle:
implementation 'com.digitalsanctuary:ds-spring-user-framework:5.1.0'Maven:
<dependency>
<groupId>com.digitalsanctuary</groupId>
<artifactId>ds-spring-user-framework</artifactId>
<version>5.1.0</version>
</dependency>Built and verified against Spring Boot 4.1.0 (compatible with 4.0.x), Java 21+.
Security
- SUF-01 — Host-header link poisoning (CWE-640): the ordinary request host is now allow-listed, not just
X-Forwarded-Host. Whenuser.security.trustedHostsis configured, a non-allow-listed host falls back to the first configured trusted host instead of being emitted into password-reset / verification links. Ordinary-host matching is now case-insensitive (RFC 4343), and blank/whitespacetrustedHostsentries are ignored. New opt-inuser.security.requireCanonicalAppUrl(defaultfalse) fails startup unlessappUrlortrustedHostsis configured (planned to become the default in a future major version). - SUF-02 — Step-up required to set an initial password on a passwordless account. A new SPI,
StepUpService, lets consuming apps require fresh proof of presence/possession beforePOST /user/setPassword. If aStepUpServicebean is present it is required (failure →401, code 6); if none is present the endpoint is disabled by default (403, code 7). Setuser.security.allowInitialPasswordSetWithoutStepUp=trueto restore the previous session-only behavior. A startup warning is logged when the endpoint is left disabled-by-default. - SUF-03 — Revoke every user session on account delete/disable, after the change commits. Deleting or disabling an account now revokes all of that user's active sessions, deferred until after the transaction commits (closing a race where a login landing mid-scan could register a surviving session). If the transaction rolls back, sessions are not revoked.
- SUF-04 — Authenticated password change participates in brute-force lockout.
POST /user/updatePasswordnow rejects a locked account up front with423 Locked, counts a wrong current password toward lockout, and resets the counter on success. Passwordless (passkey-only / OAuth-only) accounts are rejected up front with400and never feed the lockout counter. - SUF-05 — Password-reset token no longer leaks via Referer or caches (CWE-598). The reset pages now set
Referrer-Policy: no-referrerandCache-Control: no-store(via a scoped interceptor over the configured reset URIs). The token-in-bodyPOST /user/savePasswordcontract is unchanged.
Fixes
- SUF-06 (docs) — Corrected the documented audit-log rotation default in
CONFIG.md(rotation stays opt-in /0by design). - SUF-06 (code hardening) —
showChangePasswordPageno longer mints anHttpSessionfor the anonymous token-validation request, and audits an invalid token asFailurerather thanSuccess.
Behavior changes (client impact)
POST /user/setPasswordis disabled by default (SUF-02). Returns403(code 7) unless aStepUpServicebean is provided (then step-up is required; failures return401, code 6), oruser.security.allowInitialPasswordSetWithoutStepUp=trueis set.POST /user/updatePasswordcan now return423 Lockedin addition to200/400. Clients treating any non-200as failure need no change. A passwordless account calling this endpoint receives400and is directed toPOST /user/setPassword.
Documentation
CONFIG.mdandMIGRATION.mdupdated forrequireCanonicalAppUrl, the ordinary-host allow-list, thesetPasswordstep-up requirement / default-disabled behavior,allowInitialPasswordSetWithoutStepUp, and/user/updatePasswordlockout participation. Both new properties are registered in the Spring configuration metadata for IDE auto-completion.
Upgrading? See the Migration Guide. Full details in the CHANGELOG.