| Titel | https://github.com/tobychui/ zoraxy <= 3.3.4 Improper Input Validation |
|---|
| Beschreibung | Path-exception rules for Basic Auth, ZorxAuth, and CAPTCHA match on raw string prefix, not a path boundary
## Summary
Three separate path-exception matchers, Basic Auth (`BasicAuthExceptionRules`), ZorxAuth SSO (`ZorxAuthExceptionRules`), and CAPTCHA (`ExceptionRules`), compare the request path against a configured prefix with a raw `strings.HasPrefix`, not a path-segment boundary. An operator who excludes `/public` from one of these controls to expose a single subtree also exposes every sibling path that shares the string as a prefix: `/publicAdminPanel`, `/public-internal`, `/publicapi/secret`, none of them the operator's intended subtree, all pass the exception check and skip the control entirely.
The forward-auth ignored-paths matcher in the same codebase (`mod/auth/sso/forward/ignoredpaths.go`) was already hardened against this exact class for issue #895, with a doc comment explaining the boundary/traversal risk. The three matchers here never got the same fix.
## Details
Basic Auth, `mod/dynamicproxy/authProviders.go`, `handleBasicAuth` (pre-fix):
```go
case AuthExceptionType_Paths:
if strings.HasPrefix(r.RequestURI, exceptionRule.PathPrefix) {
return nil
}
```
ZorxAuth SSO, same file, `handleZorxAuth` (pre-fix):
```go
if strings.HasPrefix(r.URL.Path, rule.PathPattern) {
return nil
}
```
CAPTCHA, `mod/dynamicproxy/captcha/captcha.go`, `CheckException` (pre-fix):
```go
case ExceptionTypePaths:
if strings.HasPrefix(requestPath, rule.PathPrefix) {
return true
}
```
The same file already has a boundary-safe matcher, `pathPrefixMatch`, used by `ShouldEnforcePath` to decide which paths CAPTCHA protects in the first place. `CheckException`, which decides which paths bypass it, was never switched to use it.
## Proof of Concept
Go test against the Basic Auth handler:
```go
func TestHandleBasicAuth_ExceptionRuleBoundary(t *testing.T) {
pe := &ProxyEndpoint{
AuthenticationProvider: &AuthenticationProvider{
AuthMethod: AuthMethodBasic,
BasicAuthCredentials: []*BasicAuthCredentials{
{Username: "admin", PasswordHash: "irrelevant-for-this-test"},
},
BasicAuthExceptionRules: []*BasicAuthExceptionRule{
{RuleType: AuthExceptionType_Paths, PathPrefix: "/public"},
},
},
}
req := httptest.NewRequest("GET", "http://example.com/publicAdminPanel/secret", nil)
w := httptest.NewRecorder()
err := handleBasicAuth(w, req, pe)
// pre-fix: err == nil, request served with no credentials
}
```
Result before the fix:
```
--- FAIL: TestHandleBasicAuth_ExceptionRuleBoundary (0.00s)
BUG: /publicAdminPanel/secret bypassed Basic Auth via the /public exception rule, no credentials required
```
The CAPTCHA test reproduces the same bypass via `captcha.CheckException` with an identical path/prefix pair.
## Impact
Any deployment using a path-based exception rule on Basic Auth, ZorxAuth SSO, or CAPTCHA gates loses the guarantee that only the intended subtree is exempted. The severity depends on what sits behind the shared prefix on the proxied application: a path-restricted exception meant to expose a health check or webhook receiver can also expose an admin panel or API that happens to share the string prefix, with no authentication or CAPTCHA challenge at all.
Affected: `github.com/tobychui/zoraxy`, confirmed on commit `f9bc2074e425a3c6da9ac9064212d2b556f5f407` (current `main`), same as `v3.3.4`.
## Fix
Basic Auth and ZorxAuth now share a new `matchesAuthExceptionPrefix` helper in `authProviders.go`, mirroring the already-hardened `forward.requestPathWithinPrefix`:
```go
func matchesAuthExceptionPrefix(requestPath string, prefix string) bool {
if prefix == "" {
return false
}
cleanedPath := path.Clean("/" + requestPath)
cleanedPrefix := path.Clean("/" + prefix)
return cleanedPath == cleanedPrefix || strings.HasPrefix(cleanedPath, cleanedPrefix+"/")
}
```
CAPTCHA's `CheckException` now calls its own existing `pathPrefixMatch` instead of a raw `HasPrefix`. New tests lock in both the fix and that the intended exception path still works: `TestMatchesAuthExceptionPrefix_Boundary`, `TestHandleBasicAuth_ExceptionRuleBoundary` (`mod/dynamicproxy`), `TestCheckException_PathBoundary` (`mod/dynamicproxy/captcha`). `go test ./mod/dynamicproxy/... ./mod/auth/...` passes after the change.
|
|---|
| Quelle | ⚠️ https://github.com/carfeii/oss/blob/main/zoraxy.md#finding-1-path-exception-rules-for-basic-auth-zorxauth-and-captcha-match-on-raw-string-prefix-not-a-path-boundary |
|---|
| Benutzer | carfeii (UID 97470) |
|---|
| Einreichung | 13.09.2026 08:58 (vor 1 Monat) |
|---|
| Moderieren | 11.10.2026 16:40 (28 days later) |
|---|
| Status | Akzeptiert |
|---|
| VulDB Eintrag | 416690 [tobychui zoraxy bis 3.3.4 Path Exception Matcher authProviders.go handleBasicAuth PathPrefix erweiterte Rechte] |
|---|
| Punkte | 20 |
|---|