Skip to content

Commit 4b95dc0

Browse files
fix: V-002 security vulnerability
Automated security fix generated by Orbis Security AI
1 parent d3ffc99 commit 4b95dc0

2 files changed

Lines changed: 57 additions & 0 deletions

File tree

auth.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,12 @@ func BasicAuthForRealm(accounts Accounts, realm string) HandlerFunc {
5252
realm = "Basic realm=" + strconv.Quote(realm)
5353
pairs := processAccounts(accounts)
5454
return func(c *Context) {
55+
// Enforce HTTPS: Basic Auth credentials are Base64-encoded, not
56+
// encrypted, and must not be transmitted over plain HTTP.
57+
if c.Request.TLS == nil && c.GetHeader("X-Forwarded-Proto") != "https" {
58+
c.AbortWithStatus(http.StatusForbidden)
59+
return
60+
}
5561
// Search user in the slice of allowed credentials
5662
user, found := pairs.searchCredential(c.requestHeader("Authorization"))
5763
if !found {
@@ -102,6 +108,12 @@ func BasicAuthForProxy(accounts Accounts, realm string) HandlerFunc {
102108
realm = "Basic realm=" + strconv.Quote(realm)
103109
pairs := processAccounts(accounts)
104110
return func(c *Context) {
111+
// Enforce HTTPS: Basic Auth credentials are Base64-encoded, not
112+
// encrypted, and must not be transmitted over plain HTTP.
113+
if c.Request.TLS == nil && c.GetHeader("X-Forwarded-Proto") != "https" {
114+
c.AbortWithStatus(http.StatusForbidden)
115+
return
116+
}
105117
proxyUser, found := pairs.searchCredential(c.requestHeader("Proxy-Authorization"))
106118
if !found {
107119
// Credentials doesn't match, we return 407 and abort handlers chain.

auth_test.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ func TestBasicAuthSucceed(t *testing.T) {
9292
w := httptest.NewRecorder()
9393
req, _ := http.NewRequest(http.MethodGet, "/login", nil)
9494
req.Header.Set("Authorization", authorizationHeader("admin", "password"))
95+
req.Header.Set("X-Forwarded-Proto", "https")
9596
router.ServeHTTP(w, req)
9697

9798
assert.Equal(t, http.StatusOK, w.Code)
@@ -111,13 +112,34 @@ func TestBasicAuth401(t *testing.T) {
111112
w := httptest.NewRecorder()
112113
req, _ := http.NewRequest(http.MethodGet, "/login", nil)
113114
req.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte("admin:password")))
115+
req.Header.Set("X-Forwarded-Proto", "https")
114116
router.ServeHTTP(w, req)
115117

116118
assert.False(t, called)
117119
assert.Equal(t, http.StatusUnauthorized, w.Code)
118120
assert.Equal(t, "Basic realm=\"Authorization Required\"", w.Header().Get("WWW-Authenticate"))
119121
}
120122

123+
func TestBasicAuth403OverHTTP(t *testing.T) {
124+
called := false
125+
accounts := Accounts{"foo": "bar"}
126+
router := New()
127+
router.Use(BasicAuth(accounts))
128+
router.GET("/login", func(c *Context) {
129+
called = true
130+
c.String(http.StatusOK, c.MustGet(AuthUserKey).(string))
131+
})
132+
133+
w := httptest.NewRecorder()
134+
req, _ := http.NewRequest(http.MethodGet, "/login", nil)
135+
req.Header.Set("Authorization", authorizationHeader("foo", "bar"))
136+
// No X-Forwarded-Proto and no TLS — must be rejected before credentials are checked.
137+
router.ServeHTTP(w, req)
138+
139+
assert.False(t, called)
140+
assert.Equal(t, http.StatusForbidden, w.Code)
141+
}
142+
121143
func TestBasicAuth401WithCustomRealm(t *testing.T) {
122144
called := false
123145
accounts := Accounts{"foo": "bar"}
@@ -131,6 +153,7 @@ func TestBasicAuth401WithCustomRealm(t *testing.T) {
131153
w := httptest.NewRecorder()
132154
req, _ := http.NewRequest(http.MethodGet, "/login", nil)
133155
req.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte("admin:password")))
156+
req.Header.Set("X-Forwarded-Proto", "https")
134157
router.ServeHTTP(w, req)
135158

136159
assert.False(t, called)
@@ -149,6 +172,7 @@ func TestBasicAuthForProxySucceed(t *testing.T) {
149172
w := httptest.NewRecorder()
150173
req, _ := http.NewRequest(http.MethodGet, "/test", nil)
151174
req.Header.Set("Proxy-Authorization", authorizationHeader("admin", "password"))
175+
req.Header.Set("X-Forwarded-Proto", "https")
152176
router.ServeHTTP(w, req)
153177

154178
assert.Equal(t, http.StatusOK, w.Code)
@@ -168,9 +192,30 @@ func TestBasicAuthForProxy407(t *testing.T) {
168192
w := httptest.NewRecorder()
169193
req, _ := http.NewRequest(http.MethodGet, "/test", nil)
170194
req.Header.Set("Proxy-Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte("admin:password")))
195+
req.Header.Set("X-Forwarded-Proto", "https")
171196
router.ServeHTTP(w, req)
172197

173198
assert.False(t, called)
174199
assert.Equal(t, http.StatusProxyAuthRequired, w.Code)
175200
assert.Equal(t, "Basic realm=\"Proxy Authorization Required\"", w.Header().Get("Proxy-Authenticate"))
176201
}
202+
203+
func TestBasicAuthForProxy403OverHTTP(t *testing.T) {
204+
called := false
205+
accounts := Accounts{"foo": "bar"}
206+
router := New()
207+
router.Use(BasicAuthForProxy(accounts, ""))
208+
router.Any("/*proxyPath", func(c *Context) {
209+
called = true
210+
c.String(http.StatusOK, c.MustGet(AuthProxyUserKey).(string))
211+
})
212+
213+
w := httptest.NewRecorder()
214+
req, _ := http.NewRequest(http.MethodGet, "/test", nil)
215+
req.Header.Set("Proxy-Authorization", authorizationHeader("foo", "bar"))
216+
// No X-Forwarded-Proto and no TLS — must be rejected before credentials are checked.
217+
router.ServeHTTP(w, req)
218+
219+
assert.False(t, called)
220+
assert.Equal(t, http.StatusForbidden, w.Code)
221+
}

0 commit comments

Comments
 (0)