reverseproxy: compare sticky-session cookie hash in constant time (#7853)
Some checks are pending
Tests / test (./cmd/caddy/caddy, ~1.26.0, macos-14, 0, 1.26, mac) (push) Waiting to run
Tests / test (./cmd/caddy/caddy, ~1.26.0, ubuntu-latest, 0, 1.26, linux) (push) Waiting to run
Tests / test (./cmd/caddy/caddy.exe, ~1.26.0, windows-latest, True, 1.26, windows) (push) Waiting to run
Tests / test (s390x on IBM Z) (push) Waiting to run
Tests / goreleaser-check (push) Waiting to run
Cross-Build / build (~1.26.0, 1.26, aix) (push) Waiting to run
Cross-Build / build (~1.26.0, 1.26, darwin) (push) Waiting to run
Cross-Build / build (~1.26.0, 1.26, dragonfly) (push) Waiting to run
Cross-Build / build (~1.26.0, 1.26, freebsd) (push) Waiting to run
Cross-Build / build (~1.26.0, 1.26, illumos) (push) Waiting to run
Cross-Build / build (~1.26.0, 1.26, linux) (push) Waiting to run
Cross-Build / build (~1.26.0, 1.26, netbsd) (push) Waiting to run
Cross-Build / build (~1.26.0, 1.26, openbsd) (push) Waiting to run
Cross-Build / build (~1.26.0, 1.26, solaris) (push) Waiting to run
Cross-Build / build (~1.26.0, 1.26, windows) (push) Waiting to run
Lint / lint (push) Waiting to run
Lint / lint-1 (push) Waiting to run
Lint / lint-2 (push) Waiting to run
Lint / govulncheck (push) Waiting to run
Lint / dependency-review (push) Waiting to run
OpenSSF Scorecard supply-chain security / Scorecard analysis (push) Waiting to run

This commit is contained in:
alhuda 2026-07-10 04:46:25 +05:30 committed by GitHub
parent 4e62095245
commit 75c988d118
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 44 additions and 1 deletions

View file

@ -712,7 +712,7 @@ func (s CookieHashSelection) Select(pool UpstreamPool, req *http.Request, w http
continue
}
sha, err := hashCookie(s.Secret, upstream.Dial)
if err == nil && sha == cookieValue {
if err == nil && hmac.Equal([]byte(sha), []byte(cookieValue)) {
return upstream
}
}

View file

@ -890,3 +890,46 @@ func TestCookieHashPolicyWithFirstFallback(t *testing.T) {
t.Error("Expected cookieHashPolicy to set a new cookie.")
}
}
func TestCookieHashPolicyWithSecret(t *testing.T) {
ctx, cancel := caddy.NewContext(caddy.Context{Context: context.Background()})
defer cancel()
cookieHashPolicy := CookieHashSelection{Secret: "hunter2"}
if err := cookieHashPolicy.Provision(ctx); err != nil {
t.Errorf("Provision error: %v", err)
t.FailNow()
}
pool := testPool()
pool[0].Dial = "localhost:8080"
pool[1].Dial = "localhost:8081"
pool[2].Dial = "localhost:8082"
pool[0].setHealthy(true)
pool[1].setHealthy(true)
pool[2].setHealthy(true)
request := httptest.NewRequest(http.MethodGet, "/test", nil)
w := httptest.NewRecorder()
h := cookieHashPolicy.Select(pool, request, w)
cookie := w.Result().Cookies()[0]
// a matching cookie sticks to the same host
request = httptest.NewRequest(http.MethodGet, "/test", nil)
w = httptest.NewRecorder()
request.AddCookie(cookie)
if got := cookieHashPolicy.Select(pool, request, w); got != h {
t.Errorf("Expected to stick to host %s, got %s", h, got)
}
if len(w.Result().Cookies()) != 0 {
t.Error("Expected no new cookie for a matching value")
}
// a tampered cookie value must not match any host and gets a fresh cookie
request = httptest.NewRequest(http.MethodGet, "/test", nil)
w = httptest.NewRecorder()
request.AddCookie(&http.Cookie{Name: cookie.Name, Value: cookie.Value[:len(cookie.Value)-1] + "0"})
cookieHashPolicy.Select(pool, request, w)
if len(w.Result().Cookies()) == 0 {
t.Error("Expected a new cookie to be set for a non-matching value")
}
}