auth: the duplicate-cookie line hides a link's token (audit A4)

Security audit 2026-10-17, finding A4 (low), stage 0. The line logged when
a request carries two unprefixed session cookies printed r.URL.Path raw,
so GET /confirm/<token> wrote the token to the log (reachable only with
PANEL_COOKIE_SECURE=false, where a sibling host can plant the bare name).
loggedPath moves from internal/web to internal/web/auth as LoggedPath
(web imports auth, not the other way) and both lines - the origin check's
refusal and the duplicate-cookie one - go through it.

Tests: TestDuplicateCookieLineHidesALinksToken; TestLoggedPathHidesTokens
moved with the function. go test ./internal/web ./internal/web/auth.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
mix
2026-10-17 21:35:32 +03:00
co-authored by Claude Opus 5.5
parent da2e8b2ffe
commit 5210cb19f2
7 changed files with 46 additions and 19 deletions
+3 -14
View File
@@ -3,7 +3,8 @@ package web
import (
"net/http"
"net/url"
"strings"
"github.com/mixeme/selfpost/internal/web/auth"
)
// contentSecurityPolicy is the panel's CSP. Everything the pages load —
@@ -97,7 +98,7 @@ func (s *Server) secure(next http.Handler) http.Handler {
// saving anything".
logf("panel: rejected %s %s: cross-origin request (Sec-Fetch-Site=%q Origin=%q Host=%q); "+
"if this was a legitimate request, check that the reverse proxy passes the original Host header through",
r.Method, loggedPath(r.URL.Path), r.Header.Get("Sec-Fetch-Site"), r.Header.Get("Origin"), r.Host)
r.Method, auth.LoggedPath(r.URL.Path), r.Header.Get("Sec-Fetch-Site"), r.Header.Get("Origin"), r.Host)
http.Error(w, "cross-origin request rejected", http.StatusForbidden)
return
}
@@ -169,15 +170,3 @@ func originAllowed(r *http.Request) bool {
}
return u.Host == r.Host
}
// loggedPath is a request's path as the log may hold it: the token of a
// password-reset link is a secret, and the log is read by more than the
// person the link went to (security review, 2026-10-17).
func loggedPath(p string) string {
for _, prefix := range []string{"/login/reset/", "/confirm/"} {
if strings.HasPrefix(p, prefix) && len(p) > len(prefix) {
return prefix + "…"
}
}
return p
}