SECURITYHIGHCVE-2026-29067

CVE-2026-29067: ZITADEL Password Reset Poisoned by Host Header Injection

A high-severity host header injection vulnerability in ZITADEL's login V2 password reset flow allows attackers to redirect reset links to...

Dylan H.

Security Team

March 8, 2026
5 min read
CVE-2026-29067: ZITADEL Password Reset Poisoned by Host Header Injection

Affected Products

  • ZITADEL 4.0.0-rc.1 - 4.7.0

ZITADEL Password Reset Flow Exploitable via Forged Host Headers

ZITADEL, a widely deployed open-source identity management platform, has patched CVE-2026-29067, a high-severity host header injection vulnerability in its login V2 password reset mechanism. The flaw allows an unauthenticated attacker to poison password reset emails, redirecting secret reset codes to an attacker-controlled domain and achieving full account takeover without ever knowing the target's current credentials.

The vulnerability carries a CVSS score of 8.1 and affects ZITADEL versions 4.0.0-rc.1 through 4.7.0. A patch is available in version 4.12.0.


Vulnerability Details

AttributeValue
CVE IDCVE-2026-29067
CVSS Score8.1 (High)
Affected VersionsZITADEL 4.0.0-rc.1 – 4.7.0
Patched VersionZITADEL 4.12.0
CWECWE-640: Weak Password Recovery Mechanism
Attack TypeHost/Forwarded Header Injection → Password Reset Poisoning
Authentication RequiredNone
User InteractionRequired (victim clicks poisoned reset link)
Disclosure DateMarch 7, 2026

How the Attack Works

The Root Cause

ZITADEL's login V2 password reset function constructs the URL embedded in reset emails by reading the Forwarded or X-Forwarded-Host HTTP headers from the incoming request. These headers are intended to communicate the original client-facing hostname when ZITADEL sits behind a reverse proxy.

However, if these headers are not stripped or validated by an upstream proxy, an attacker can inject an arbitrary hostname into the request. ZITADEL will then embed that attacker-controlled domain into the password reset confirmation URL — and send it to the victim's email address.

Attack Chain

1. Attacker identifies a valid user email address in the target ZITADEL deployment
2. Attacker sends a password reset request to the ZITADEL API
   — includes a forged "X-Forwarded-Host: attacker.example.com" header
3. ZITADEL constructs the reset URL using the forged hostname:
   https://attacker.example.com/ui/login/password/reset?code=SECRET_TOKEN
4. ZITADEL emails the victim the poisoned reset link
5. Victim clicks the link — browser navigates to attacker.example.com
6. Attacker's server captures the SECRET_TOKEN from the URL
7. Attacker uses the SECRET_TOKEN to reset the victim's password
8. Attacker logs in as the victim — full account takeover achieved

Why This Is Dangerous

The attack is entirely server-side — the attacker never needs to be on the victim's network, intercept traffic, or exploit a client-side vulnerability. It requires only:

  • Knowledge of a valid user email address (often obtainable via OSINT or user enumeration)
  • The ability to send an HTTP request with a forged header (trivially achievable)
  • The victim to click a link in an email (standard user behavior for password resets)

If the target account is protected by MFA or Passwordless authentication, the attacker will still capture the reset token but may not be able to complete a login without the second factor — making MFA an effective mitigating control even though it does not prevent the token capture itself.


Affected Deployments

ScenarioRisk Level
Self-hosted ZITADEL without a hardening proxyCritical — headers reach ZITADEL unfiltered
Self-hosted with proxy that strips Forwarded headersMitigated
Cloud-hosted ZITADEL (vendor-managed)Lower — vendor controls proxy configuration
Deployments on 4.0.0-rc.1 to 4.7.0Vulnerable — upgrade required
Deployments on 4.12.0+Patched

Remediation

ZITADEL 4.12.0 adds validation of the Forwarded and X-Forwarded-Host headers against a configured allowlist of trusted hostnames, preventing injection of arbitrary values.

# Pull patched Docker image
docker pull ghcr.io/zitadel/zitadel:v4.12.0
 
# Kubernetes Helm upgrade
helm upgrade zitadel zitadel/zitadel --set image.tag=v4.12.0

Option 2: Strip Forwarded Headers at the Proxy Layer

Configure your reverse proxy (Nginx, Caddy, Traefik, HAProxy) to delete or override the Forwarded and X-Forwarded-Host headers before forwarding requests to ZITADEL:

# Nginx — strip and re-set forwarded headers
proxy_set_header Forwarded "";
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
# Traefik middleware — strip headers
http:
  middlewares:
    strip-forwarded:
      headers:
        customRequestHeaders:
          Forwarded: ""
          X-Forwarded-Host: ""

Option 3: Enforce MFA Across All Accounts

While MFA does not prevent the reset token from being captured, it blocks the attacker from completing authentication after resetting the password. Enforce MFA organization-wide as a layered defense.


Detection

Review ZITADEL access logs or upstream proxy logs for password reset requests that include suspicious X-Forwarded-Host values:

# Check for password reset requests with non-standard host headers
grep "POST.*password.*reset\|POST.*ui/login/password" /var/log/nginx/access.log | \
  grep -v "yourdomain.com"
 
# ZITADEL application logs — look for reset requests
journalctl -u zitadel | grep -i "password reset" | grep -v "yourdomain.com"

Three other ZITADEL vulnerabilities were disclosed on the same date:

CVECVSSDescription
CVE-2026-291919.3 CriticalXSS in /saml-post — 1-click account takeover
CVE-2026-290678.1 HighPassword reset host header injection
CVE-2026-291927.7 HighStored XSS via Default URI Redirect
CVE-2026-29193TBDLogin V2 policy bypass — unauthorized self-registration

All are resolved in ZITADEL version 4.12.0.


Sources