Skip to content

feat(core): throttle ActiveSync auth and harden user resolution#187

Merged
ralflang merged 1 commit into
FRAMEWORK_6_0from
security/activesync-hardening
Jul 2, 2026
Merged

feat(core): throttle ActiveSync auth and harden user resolution#187
ralflang merged 1 commit into
FRAMEWORK_6_0from
security/activesync-hardening

Conversation

@TDannhauer

Copy link
Copy Markdown
Contributor

Summary

  • Add a cache-backed brute-force throttle to the ActiveSync driver's authenticate().
  • Harden getUser() so an authenticated identity always takes precedence over the client-supplied ?User= query parameter.

Motivation

The ActiveSync endpoint runs a full backend authentication on every request but had no failure throttling, so it could be used for credential guessing in a way that bypasses Horde's interactive-login protection. Separately, getUser() consulted the client-controlled ?User= query parameter before the registry-authenticated identity, which is fragile for a value that is not proof of identity.

Changes

  • Brute-force throttle (Horde_Core_ActiveSync_Driver::authenticate()): count credential failures per (username, client IP) in the cache and refuse further attempts once a threshold is reached within a window. A successful credential check clears the counter, so normally-configured devices never accumulate. Tunable via $conf['activesync']['auth']['throttle'] (enabled, max_attempts default 15, window default 300s). Automatically inert when no cache backend is configured, so existing deployments without a cache are unaffected. When tripped it returns AUTH_REASON_UNAVAILABLE so the client backs off.
  • User resolution (getUser()): reorder precedence to (1) authenticated user, (2) registry-authenticated user, (3) ?User= query parameter. The ?User= value is now only a last resort for the transparent X509 provisioning flow, is logged, and is documented as not proof of identity (the certificate path in authenticate() verifies it against the certificate-authenticated identity, clearing the session on mismatch, before any data is served).
  • Update DriverGetUserTest to assert the new precedence (registry over ?User=).

Test plan

  • php -l clean on changed files
  • test/Unit/ActiveSync passes (22/22)
  • Manual: repeated bad-password ActiveSync logins get throttled with a cache backend configured; a correct login clears the counter
  • Manual: X509 certificate provisioning flow still resolves the user correctly

Add a cache-backed brute-force throttle to the ActiveSync driver's
authenticate(): credential failures are counted per (username, client IP)
and further attempts are refused once a configurable threshold is reached
within a window, closing the gap where the ActiveSync endpoint bypassed
Horde's interactive-login protection. A successful credential check clears
the counter, so normally-configured devices never accumulate. Tunable via
$conf['activesync']['auth']['throttle'] (enabled, max_attempts, window);
automatically inert when no cache backend is configured.

Harden getUser() so an authenticated identity (auth flow, then registry)
always takes precedence over the client-controlled ?User= query parameter.
The ?User= value is now only a last resort for the transparent X509
provisioning flow, is logged, and is documented as not being proof of
identity (the certificate path verifies it before serving data). Update
the getUser() precedence unit tests accordingly.
@TDannhauer TDannhauer requested a review from ralflang July 2, 2026 06:29
@ralflang ralflang merged commit 3390550 into FRAMEWORK_6_0 Jul 2, 2026
1 check failed
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.

2 participants