# kin-openapi: ValidationHandler.Load() Fail-Open Authentication Bypass via NoopAuthenticationFunc Default

kin-openapi's ValidationHandler.Load() silently substitutes NoopAuthenticationFunc for a nil AuthenticationFunc, so OpenAPI security requirements pass and unauthenticated requests reach protected handlers.

- Payout: Research
- Severity: critical
- Program: kin-openapi
- Found by: EQSTLab, 232-323
- Disclosed: 2026-07-24
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-r277-6w6q-xmqw
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-r277-6w6q-xmqw
- Tags: area:web domain:api domain:oss impact:data-leak impact:integrity kind:research platform:ghsa sev:critical tech:go technique:source-review vuln:access-control vuln:auth-bypass

**Target:** Go HTTP middleware (openapi3filter.ValidationHandler)
**Root cause:** Load() unconditionally sets h.AuthenticationFunc = NoopAuthenticationFunc when nil, and the no-op returns nil for every security scheme, so ValidateRequest's fail-closed ErrAuthenticationServiceMissing guard is never reached.
**Steps:**
1. Build PoC image from kin-openapi v0.143.0 source with a Go program using ValidationHandler.
2. Define OpenAPI spec with `GET /secret` requiring `security: [{apiKey: []}]` (header X-Api-Key).
3. Construct ValidationHandler without setting AuthenticationFunc and call Load().
4. Send `GET /secret` with no X-Api-Key header.
5. Observe status=200 and body SECRET_DATA; contrast direct ValidateRequest with nil auth returns 'missing AuthenticationFunc'.
**How found:** Code reading of validation_handler.go:47-49 spotted the nil-to-NoopAuthenticationFunc substitution, then contrasted it with ValidateRequest's fail-closed nil check to confirm the fail-open default.
**Exploitation:** Any app using ValidationHandler as middleware with OpenAPI security declarations and no explicit AuthenticationFunc lets an unauthenticated remote attacker hit every protected route; the no-op approves each security requirement and the request is forwarded to the underlying handler.
**Insight:** When a library offers both a low-level validator and a high-level middleware wrapper, diff their default handling of security callbacks: a wrapper that fills nil with a no-op silently converts a fail-closed primitive into fail-open.

## Proof of concept (copied exactly from the original report)

**Dockerfile building the auth-bypass PoC binary**

```
FROM golang:1.25-alpine

# Install git (needed by go mod for some packages)
RUN apk add --no-cache git

WORKDIR /workspace

# Copy the vulnerable kin-openapi repository as a local module replacement
COPY repo/ /kin-openapi/

# Set up the PoC Go module
RUN mkdir -p /workspace/poc
WORKDIR /workspace/poc

# Create go.mod that uses the local copy of the vulnerable kin-openapi
RUN cat > go.mod <<'EOF'
module kin-openapi-auth-bypass-poc

go 1.25

require github.com/getkin/kin-openapi v0.143.0

replace github.com/getkin/kin-openapi => /kin-openapi
EOF

# Copy the PoC source (build context is the parent directory of vuln-001/)
COPY vuln-001/main.go /workspace/poc/main.go

# Resolve dependencies and build
RUN go mod tidy && \
    go build -o /poc-binary .

# Run the PoC
CMD ["/poc-binary"]
```

