Skip to content

Security: password-reset/verification links can derive authority from an untrusted Host header (SUF-01) #327

Description

@devondragon

Summary

When user.security.appUrl is not configured, AppUrlResolver derives the authority for outbound email links (password reset and registration verification) from the incoming request without a complete allowlist check. On common servlet containers this authority is influenced by the Host header, so the links embedded in recovery/verification emails can point at a host the operator did not intend.

Severity: High (insecure-by-default; fully mitigated by setting user.security.appUrl)

Affected code

  • src/main/java/com/digitalsanctuary/spring/user/util/AppUrlResolver.java:73 — the non-forwarded fallback uses request.getServerName() with no trustedHosts validation, whereas X-Forwarded-Host is validated against trustedHosts.
  • src/main/java/com/digitalsanctuary/spring/user/service/UserEmailService.java:182 — the emailed link is built from the resolved app URL.
  • src/main/resources/config/dsspringuserconfig.properties:83,85appUrl and trustedHosts both ship empty.
  • /user/resetPassword and /user/registration are unauthenticated by default.

Details

resolveAppUrl() short-circuits and returns appUrl when it is configured. When it is blank, it falls through to request-derived resolution: a forwarded host is only used if it passes the trustedHosts allowlist, but the ordinary server name path applies no such check. On stock Tomcat/Jetty/Undertow, getServerName() reflects the Host header for unrecognized hosts, so the resulting link authority is externally influenced.

Impact

A single-use bearer token delivered in a verification/reset email could be rendered into a link whose host is not the canonical application host, undermining the recovery mechanism (CWE-640).

Recommended fix

  • Require a canonical user.security.appUrl whenever recovery/verification email is enabled; prefer failing startup if it is absent.
  • Validate it as absolute HTTPS in production (no user-info, query, or fragment).
  • If request-derived fallback must remain, apply the same allowlist to the ordinary host as to the forwarded host.
  • AppUrlResolverTest currently encodes the permissive fallback (accepts an arbitrary non-forwarded server name); update alongside the fix.

Identified during a code-first security assessment of the current main branch. Reported without an exploit recipe per public-repo disclosure hygiene.

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