gofr-dev / gofr-dev/gofr

GoFr — Auth middleware exempts the entire /.well-known prefix, leading to authentication bypass

Open
#3,978 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
Go
Stars
20.9k
Forks
1.8k
Avg merge
5d 18h
Merged PRs (30d)
39

Description

# GoFr — Authentication bypass via /.well-known prefix exemption in AuthMiddleware leads to anonymous access to protected routes and health data

## Summary

GoFr's unified authentication middleware (`AuthMiddleware`) calls `isWellKnown(r.URL.Path)` before extracting any authentication header, and `isWellKnown` is implemented as `strings.HasPrefix(path, "/.well-known")`. This exempts the entire `/.well-known` prefix — not just an intended probe path — so the built-in `/.well-known/health` endpoint (which the official documentation acknowledges may expose dependency service hosts and statistics) remains anonymously reachable even after Basic Auth, API Key, or OAuth is enabled. In addition, any user-registered route whose path merely starts with `/.well-known` (e.g. `/.well-knownprivate` — no slash is required to match the prefix) silently skips authentication, and because the same `isWellKnown` check is reused by the rate limiter, these paths bypass rate limiting as well.

**Weakness:** CWE-306: Missing Authentication for Critical Function (secondary: CWE-200: Exposure of Sensitive Information)

## Root Cause

In GoFr v1.58.0:

1. `pkg/gofr/http/middleware/validate.go:5-7` — `isWellKnown` is implemented as `strings.HasPrefix(path, "/.well-known")`. It is a raw prefix match that does not require the prefix to be followed by `/`, so paths such as `/.well-knownprivate` also match.
2. `pkg/gofr/http/middleware/auth.go:49-52` — `AuthMiddleware` evaluates `isWellKnown(r.URL.Path)` before `ExtractAuthHeader` and, on a match, passes the request directly to `handler.ServeHTTP`, skipping authentication entirely. All official auth mechanisms (Basic Auth, API Key, OAuth) share this middleware, so every one of them is bypassed for matching paths.
3. `pkg/gofr/http/middleware/rate_limiter.go:133-136` — the same `isWellKnown` check is reused in the rate limiter, so matching paths also skip rate limiting.

## Impact

- An anonymous attacker can read the built-in `/.well-known/health` endpoint of applications that have authentication enabled, leaking internal dependency topology (database/cache hosts, status, statistics).
- Developers cannot protect any route under `/.well-known*` using GoFr's official authentication capability; business routes registered with that prefix — knowingly or unknowingly — are exposed directly.
- Matching paths additionally bypass the rate limiter, removing throttling protection from those endpoints.

## Prerequisites

- Anonymous network access to a GoFr application that has any official authentication enabled (Basic Auth, API Key, or OAuth). No account or credentials are needed.
- For the health-data disclosure: the application serves the built-in `/.well-known/health` endpoint (framework default).
- For the custom-route bypass: the application has registered at least one route whose path starts with `/.well-known`.

## Steps to Reproduce

1. Using a Go toolchain, create the application from the Proof of Concept code below: a GoFr app with API Key authentication enabled, registering `GET /protected` and `GET /.well-knownprivate`.
2. Run the application (it listens on port 8000 by default).
3. Request the ordinary protected route without credentials: `curl -i http://localhost:8000/protected` — observe `401 Unauthorized`.
4. Request the registered route that starts with `/.well-known` without credentials: `curl -i http://localhost:8000/.well-knownprivate` — observe `200` with the body `SHOULD-BE-PROTECTED`: authentication is bypassed by the prefix exemption.
5. Request the built-in health endpoint without credentials: `curl -i http://localhost:8000/.well-known/health` — observe `200` with dependency information returned anonymously.

## Proof of Concept

The following self-contained program demonstrates the bypass. Running it requires a Go toolchain; the behavior was verified by source-code review of the middleware chain. Basic Auth and OAuth are affected identically, since all auth methods share `AuthMiddleware`.

```go
// GoFr authentication bypass PoC — gofr v1.58.0
package main

import (
"gofr.dev/pkg/gofr"
)

func main() {
app := gofr.New()

// Enable official API Key auth (Basic/OAuth are affected as well — they share AuthMiddleware)
app.EnableAPIKeyAuth("my-secret-key")

app.GET("/protected", func(ctx *gofr.Context) (any, error) {
return "PROTECTED-DATA", nil
})

// Note: no slash needed — "/.well-knownprivate" still matches HasPrefix("/.well-known")
app.GET("/.well-knownprivate", func(ctx *gofr.Context) (any, error) {
return "SHOULD-BE-PROTECTED", nil
})

app.Run()
}

// Reproduce:
// curl -i http://localhost:8000/protected -> 401
// curl -i http://localhost:8000/.well-knownprivate -> 200 SHOULD-BE-PROTECTED (authentication bypass)
// curl -i http://localhost:8000/.well-known/health -> 200 (dependency info anonymously readable)
```

## Evidence

Source-code evidence (GoFr v1.58.0; the identical code path is also present on main @ commit 3e1265c3a4 — both checked 2026-07-21):

- `pkg/gofr/http/middleware/validate.go:5-7` — the exemption predicate is a plain prefix match with no `/` boundary requirement:
`isWellKnown` = `strings.HasPrefix(path, "/.well-known")`
- `pkg/gofr/http/middleware/auth.go:49-52` — on a prefix match, `AuthMiddleware` hands the request directly to `handler.ServeHTTP` before ever invoking `ExtractAuthHeader`, so no authentication mechanism is applied to matching paths.
- `pkg/gofr/http/middleware/rate_limiter.go:133-136` — the same prefix check causes matching paths to skip rate limiting.

Vendor corroboration (public):

- Issue #2550 was labeled a bug by the maintainers, who stated that only `alive` should be public and that `health`, which contains database information, should not be.
- PR #2565 attempted to narrow the exemption but was closed for compatibility reasons.
- Issue #2562 again confirmed the full-prefix bypass behavior.

## Affected Version

Verified on GoFr v1.58.0 and on main @ commit 3e1265c3a4 (both checked 2026-07-21); the code path is still present in the latest version.

## CVSS

**CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N — 5.3 (Medium)**

- AV:N — exploitable over the network with plain HTTP requests.
- AC:L — no special conditions; the bypass is decided by a simple string-prefix check.
- PR:N — no credentials or prior authentication required.
- UI:N — no user interaction is needed.
- S:U — impact is confined to the vulnerable application itself.
- C:L — anonymous read access to dependency/health information and to routes that should be protected; limited confidentiality loss.
- I:N — no integrity impact.
- A:N — no availability impact.

## Remediation

1. Change `isWellKnown` from prefix matching to exact-path matching, exempting only `/.well-known/alive` (or an explicit allowlist of safe paths).
2. Bring `/.well-known/health` under authentication by default, or provide an explicit configuration switch for exposing it.
3. Add regression tests asserting that an unauthenticated request to a prefixed route such as `/.well-knownprivate` returns 401.

## References

- https://gofr.dev/docs/advanced-guide/authentication
- https://github.com/gofr-dev/gofr/issues/2550
- https://github.com/gofr-dev/gofr/pull/2565
- https://github.com/gofr-dev/gofr/issues/2562

Contributor guide

Open the contributing guide

Research direction

Start by reading pkg/gofr/http/middleware/validate.go, auth.go, and rate_limiter.go, then run the Go proof of concept to observe authentication and rate-limit bypasses. Check the existing well-known endpoint behavior and add regression coverage for /.well-knownprivate and /.well-known/health; done means only the intended public path remains exempt and protected prefixed routes require authentication.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
authentication, backend, security
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
65/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.