Skip to content

Commit 0ef7011

Browse files
adnaanclaude
andcommitted
fix(auth): add CSRF protection and secure cookie settings
Address Copilot PR review comments: - Add Origin/Referer validation to HandlePasswordLogin for CSRF protection - Update SetSession to use Secure: true and SameSite: Strict for auth cookies - Update tests to verify secure cookie settings 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent a55b2b5 commit 0ef7011

3 files changed

Lines changed: 33 additions & 2 deletions

File tree

internal/kits/system/multi/templates/auth/handler.go.tmpl

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -597,6 +597,26 @@ func (c *{{.StructName}}Controller) HandlePasswordLogin(w http.ResponseWriter, r
597597
return
598598
}
599599

600+
// Basic CSRF protection: validate Origin/Referer header
601+
origin := r.Header.Get("Origin")
602+
referer := r.Header.Get("Referer")
603+
host := r.Host
604+
if origin != "" {
605+
// Check Origin header matches host
606+
if !strings.Contains(origin, host) && !strings.HasPrefix(origin, "http://localhost") && !strings.HasPrefix(origin, "http://127.0.0.1") {
607+
log.Printf("CSRF protection: Origin mismatch (origin=%s, host=%s)", origin, host)
608+
http.Redirect(w, r, "/auth?error=invalid_form", http.StatusSeeOther)
609+
return
610+
}
611+
} else if referer != "" {
612+
// Fallback to Referer if Origin not present
613+
if !strings.Contains(referer, host) && !strings.HasPrefix(referer, "http://localhost") && !strings.HasPrefix(referer, "http://127.0.0.1") {
614+
log.Printf("CSRF protection: Referer mismatch (referer=%s, host=%s)", referer, host)
615+
http.Redirect(w, r, "/auth?error=invalid_form", http.StatusSeeOther)
616+
return
617+
}
618+
}
619+
600620
// Parse form data
601621
if err := r.ParseForm(); err != nil {
602622
http.Redirect(w, r, "/auth?error=invalid_form", http.StatusSeeOther)

pkg/cookie/cookie.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,12 @@ func ClearLiveTemplateSession(w http.ResponseWriter) {
9696
})
9797
}
9898

99-
// SetSession sets a session cookie with appropriate security settings.
99+
// SetSession sets a session cookie with strict security settings.
100100
// The maxAgeDays parameter specifies how long the session should last.
101+
// Uses HttpOnly, Secure, and SameSite=Strict for authentication cookies.
102+
//
103+
// Note: Secure=true requires HTTPS. For localhost HTTP development,
104+
// browsers may still accept the cookie on localhost domains.
101105
//
102106
// Example:
103107
//
@@ -109,7 +113,8 @@ func SetSession(w http.ResponseWriter, name, value string, maxAgeDays int) {
109113
Path: "/",
110114
MaxAge: maxAgeDays * 24 * 60 * 60,
111115
HttpOnly: true,
112-
SameSite: http.SameSiteLaxMode,
116+
Secure: true,
117+
SameSite: http.SameSiteStrictMode,
113118
})
114119
}
115120

pkg/cookie/cookie_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,12 @@ func TestSetSession(t *testing.T) {
110110
if c.MaxAge != expectedMaxAge {
111111
t.Errorf("expected MaxAge %d, got %d", expectedMaxAge, c.MaxAge)
112112
}
113+
if !c.Secure {
114+
t.Error("expected Secure to be true")
115+
}
116+
if c.SameSite != http.SameSiteStrictMode {
117+
t.Errorf("expected SameSite %v, got %v", http.SameSiteStrictMode, c.SameSite)
118+
}
113119
}
114120

115121
func TestGet(t *testing.T) {

0 commit comments

Comments
 (0)