Skip to content

Add HealthCheck middleware (full rescan) - #4

Open
setlin-hacktron wants to merge 10 commits into
base/pr-620from
healthcheck-fullscan-2026-07-08
Open

Add HealthCheck middleware (full rescan)#4
setlin-hacktron wants to merge 10 commits into
base/pr-620from
healthcheck-fullscan-2026-07-08

Conversation

@setlin-hacktron

Copy link
Copy Markdown
Owner

Fresh PR to trigger a full-scope Hacktron PR scan (opened event) after rebuilding the local sandbox image. Same vulnerable diff as #2/#3 (base base/pr-620, head at pre-fix commit 4727bcf4).

@hacktron-app-dev hacktron-app-dev Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 1 file

Severity Count
CRITICAL 1

View full scan results

Comment on lines +40 to +48
func isHealthCheckRequest(paths, userAgents map[string]struct{}, req *http.Request) bool {
if _, ok := paths[req.URL.EscapedPath()]; ok {
return true
}
if _, ok := userAgents[req.Header.Get("User-Agent")]; ok {
return true
}
return false
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CRITICAL Authentication Bypass via User-Agent-based Health Check in Reverse Proxy / Forward Auth Deployments

The oauth2-proxy application implements a health check middleware (pkg/middleware/healthcheck.go) that globally intercepts all incoming HTTP requests before any routing or authentication handlers are executed.

If the incoming request's User-Agent matches any of the configured health check User-Agents (such as "GoogleHC/1.0" when GCPHealthChecks is enabled, or the user-configured PingUserAgent), the middleware immediately returns 200 OK with the body "OK" and short-circuits the handler chain.

In forward-authentication or reverse-proxy auth architectures (such as Nginx auth_request, Traefik ForwardAuth, or Envoy ExtAuthz), the reverse proxy delegates authentication decisions to oauth2-proxy by forwarding client requests (including client headers like User-Agent) to an auth endpoint (e.g., /oauth2/auth). An unauthenticated attacker can set their User-Agent to a configured health check User-Agent, causing oauth2-proxy to return 200 OK. The reverse proxy interprets this as a successful authentication and grants the attacker access to the protected resource.

Furthermore, by default, opts.PingUserAgent is an empty string (""). Because the application does not filter out empty strings when constructing the trusted User-Agent set, any request that lacks a User-Agent header (or has an empty one) will match "" and bypass authentication entirely by default.

Steps to Reproduce
  1. Deploy oauth2-proxy in a forward-authentication setup (e.g., using Nginx auth_request pointing to /oauth2/auth).
  2. Send an HTTP request to a protected endpoint (e.g., /admin) with either:
    a. The User-Agent header set to a configured health check User-Agent (e.g., GoogleHC/1.0 if GCP health checks are enabled).
    b. No User-Agent header at all (if ping-user-agent is left at its default empty value).
  3. The health check middleware will match the User-Agent and immediately return 200 OK.
  4. Nginx will interpret the 200 OK response as successful authentication and forward the request to the upstream service, bypassing authentication entirely.
# Bypass authentication when GCPHealthChecks is enabled
curl -H "User-Agent: GoogleHC/1.0" https://protected-app.example.com/admin

# Bypass authentication by default (when no PingUserAgent is configured, matching the empty string)
curl -H "User-Agent:" https://protected-app.example.com/admin
Fix with AI

Open in Cursor Open in Claude

A security vulnerability was found by Hacktron.

File: pkg/middleware/healthcheck.go
Lines: 40-48
Severity: critical

Vulnerability: Authentication Bypass via User-Agent-based Health Check in Reverse Proxy / Forward Auth Deployments

Description:
The `oauth2-proxy` application implements a health check middleware (`pkg/middleware/healthcheck.go`) that globally intercepts all incoming HTTP requests before any routing or authentication handlers are executed. 

If the incoming request's `User-Agent` matches any of the configured health check User-Agents (such as `"GoogleHC/1.0"` when `GCPHealthChecks` is enabled, or the user-configured `PingUserAgent`), the middleware immediately returns `200 OK` with the body `"OK"` and short-circuits the handler chain.

In forward-authentication or reverse-proxy auth architectures (such as Nginx `auth_request`, Traefik `ForwardAuth`, or Envoy `ExtAuthz`), the reverse proxy delegates authentication decisions to `oauth2-proxy` by forwarding client requests (including client headers like `User-Agent`) to an auth endpoint (e.g., `/oauth2/auth`). An unauthenticated attacker can set their `User-Agent` to a configured health check User-Agent, causing `oauth2-proxy` to return `200 OK`. The reverse proxy interprets this as a successful authentication and grants the attacker access to the protected resource.

Furthermore, by default, `opts.PingUserAgent` is an empty string (`""`). Because the application does not filter out empty strings when constructing the trusted `User-Agent` set, any request that lacks a `User-Agent` header (or has an empty one) will match `""` and bypass authentication entirely by default.

Proof of Concept:
**Steps to Reproduce**

1. Deploy `oauth2-proxy` in a forward-authentication setup (e.g., using Nginx `auth_request` pointing to `/oauth2/auth`).
2. Send an HTTP request to a protected endpoint (e.g., `/admin`) with either:
   a. The `User-Agent` header set to a configured health check User-Agent (e.g., `GoogleHC/1.0` if GCP health checks are enabled).
   b. No `User-Agent` header at all (if `ping-user-agent` is left at its default empty value).
3. The health check middleware will match the `User-Agent` and immediately return `200 OK`.
4. Nginx will interpret the `200 OK` response as successful authentication and forward the request to the upstream service, bypassing authentication entirely.

```bash
# Bypass authentication when GCPHealthChecks is enabled
curl -H "User-Agent: GoogleHC/1.0" https://protected-app.example.com/admin

# Bypass authentication by default (when no PingUserAgent is configured, matching the empty string)
curl -H "User-Agent:" https://protected-app.example.com/admin
```

Affected Code:
func isHealthCheckRequest(paths, userAgents map[string]struct{}, req *http.Request) bool {
	if _, ok := paths[req.URL.EscapedPath()]; ok {
		return true
	}
	if _, ok := userAgents[req.Header.Get("User-Agent")]; ok {
		return true
	}
	return false
}

Acceptance criteria:
- Acceptance is defined by the **actual reported behavior**, not by tests passing.
- Reproduce the issue, or narrow the exact code path that produces it, *before* changing code. State what you confirmed.
- Fix the underlying cause. Mitigations that paper over the reported behavior do not count as a fix.
- Add a regression test that fails on the unpatched code and passes on the fix. If a regression test is genuinely impractical (e.g. race condition, infra-level issue), say so and explain why.
- Existing tests passing is **not** the bar. Do not declare done on tests-pass theatre.

Only change what is necessary to fix this vulnerability. Do not refactor adjacent code or modify unrelated files.

Triage: Reply !fp <reason> (false positive), !valid (confirmed), !accepted_risk <reason>, or !fixed (resolved). Any other reply is saved as a triage note.
Reason is optional but improves future scans — e.g. !fp internal endpoint, not user-facing.

View finding in Hacktron

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.

1 participant