fix: always set Secure/HttpOnly/SameSite on session cookies (closes #47)
All checks were successful
check / check (push) Successful in 2m4s
All checks were successful
check / check (push) Successful in 2m4s
Resolve the two remaining gosec G124 findings (internal/session/ session.go:84 and :128): session cookies are now unconditionally Secure, HttpOnly, and SameSite=Strict on both the CreateSession set-cookie path and the ClearSession delete-cookie path. gosec requires these attributes to be constant, and there is no legitimate configuration in which the authentication cookie should be weaker, so the former secure toggle (wired to !config.Debug) is removed rather than kept as a variable. The toggle parameter on NewManager is retained as an ignored blank parameter so existing call sites (including tests) keep compiling; removing it is tracked as a Future Step in TODO.md. Local development over http://localhost keeps working because browsers treat localhost as a trustworthy origin and accept Secure cookies there. Update TODO.md per its Workflow section: record this step as completed, promote the manual auth/URL-flow test pass to Next Step, and correct the stale Status text (make check is now green).
This commit is contained in:
33
TODO.md
33
TODO.md
@@ -10,22 +10,27 @@
|
|||||||
|
|
||||||
# Status
|
# Status
|
||||||
|
|
||||||
pre-1.0. No git tags exist. main (6b4a1d7, 2026-04-07) is current with
|
pre-1.0. No git tags exist. Recent work extracted the internal/magic,
|
||||||
origin; recent work extracted the internal/magic and internal/allowlist
|
internal/allowlist, internal/httpfetcher, and internal/signature
|
||||||
packages. The 10 gosec findings from the 2026-07-06 morning survey are
|
packages. The gosec findings from the 2026-07-06 survey are resolved:
|
||||||
NOT fixed on main: the newest gosec/lint fix commits are from
|
the last two open findings (G124, session cookie attributes in
|
||||||
2026-02-25 (85729d9, ce6db76) and nothing since touches gosec, so those
|
internal/session) are fixed as of this change, so `make check` is green
|
||||||
findings remain open.
|
on main.
|
||||||
|
|
||||||
# Next Step
|
# Next Step
|
||||||
|
|
||||||
Fix the 10 open gosec lint findings so make check passes on main again
|
P0: manual test pass of the auth and encrypted URL flows, then commit
|
||||||
(main must always be green; no gosec fix commits have landed since
|
the checked-off results to TODO.md: visit / and see the login form;
|
||||||
2026-02-25). No blanket suppressions; fix or justify each finding
|
wrong key shows an error; correct signing key shows the generator form;
|
||||||
individually.
|
a generated encrypted URL serves the image; an expired URL (short TTL)
|
||||||
|
returns 410; logout redirects back to login
|
||||||
|
|
||||||
# Completed Steps
|
# Completed Steps
|
||||||
|
|
||||||
|
- 2026-08-07 fix the two remaining gosec findings (G124 in
|
||||||
|
internal/session): session cookies now always carry
|
||||||
|
Secure/HttpOnly/SameSite=Strict on both the set and clear paths;
|
||||||
|
`make check` green (closes #47)
|
||||||
- 2026-07-07 Adopted scripts-to-rule-them-all: `script/` entrypoints,
|
- 2026-07-07 Adopted scripts-to-rule-them-all: `script/` entrypoints,
|
||||||
Makefile shims, README Entrypoints section
|
Makefile shims, README Entrypoints section
|
||||||
- 2026-04-07 extract magic byte detection into internal/magic (#42)
|
- 2026-04-07 extract magic byte detection into internal/magic (#42)
|
||||||
@@ -48,14 +53,12 @@ individually.
|
|||||||
|
|
||||||
# Future Steps
|
# Future Steps
|
||||||
|
|
||||||
- P0: manual test pass of the auth and encrypted URL flows, then
|
|
||||||
commit the checked-off results to TODO.md: visit / and see the login
|
|
||||||
form; wrong key shows an error; correct signing key shows the
|
|
||||||
generator form; a generated encrypted URL serves the image; an
|
|
||||||
expired URL (short TTL) returns 410; logout redirects back to login
|
|
||||||
- P0: implement cache size management and eviction so the disk cannot
|
- P0: implement cache size management and eviction so the disk cannot
|
||||||
fill up
|
fill up
|
||||||
- P0: validate configuration on startup, fail fast on bad config
|
- P0: validate configuration on startup, fail fast on bad config
|
||||||
|
- P1: remove the ignored former secure toggle parameter from
|
||||||
|
session.NewManager and update its call sites (requires touching
|
||||||
|
existing tests; needs approval per repo rules)
|
||||||
- P1: implement blocked networks configuration to extend SSRF
|
- P1: implement blocked networks configuration to extend SSRF
|
||||||
protection
|
protection
|
||||||
- P1: rate limit global concurrent upstream fetches to prevent
|
- P1: rate limit global concurrent upstream fetches to prevent
|
||||||
|
|||||||
@@ -94,8 +94,10 @@ func (s *Handlers) initImageService() error {
|
|||||||
s.imgSvc = svc
|
s.imgSvc = svc
|
||||||
s.log.Info("image service initialized")
|
s.log.Info("image service initialized")
|
||||||
|
|
||||||
// Initialize session manager (signing key is validated at config load time)
|
// Initialize session manager (signing key is validated at config load
|
||||||
sessMgr, err := session.NewManager(s.config.SigningKey, !s.config.Debug)
|
// time). The second argument is ignored: session cookies are always
|
||||||
|
// Secure/HttpOnly/SameSite=Strict.
|
||||||
|
sessMgr, err := session.NewManager(s.config.SigningKey, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,13 +37,17 @@ type Data struct {
|
|||||||
// Manager handles session creation and validation using encrypted cookies.
|
// Manager handles session creation and validation using encrypted cookies.
|
||||||
type Manager struct {
|
type Manager struct {
|
||||||
sc *securecookie.SecureCookie
|
sc *securecookie.SecureCookie
|
||||||
secure bool // Set Secure flag on cookies (should be true in production)
|
|
||||||
sameSite http.SameSite
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewManager creates a session manager with keys derived from the signing key.
|
// NewManager creates a session manager with keys derived from the signing key.
|
||||||
// Set secure=true in production to require HTTPS for cookies.
|
//
|
||||||
func NewManager(signingKey string, secure bool) (*Manager, error) {
|
// Session cookies always carry the Secure, HttpOnly, and SameSite=Strict
|
||||||
|
// attributes; this cannot be configured. Browsers treat http://localhost as a
|
||||||
|
// trustworthy origin and accept Secure cookies there, so local development
|
||||||
|
// keeps working. The second parameter is the former secure toggle: it is
|
||||||
|
// ignored and retained only so existing call sites keep compiling; it will be
|
||||||
|
// removed in a follow-up change.
|
||||||
|
func NewManager(signingKey string, _ bool) (*Manager, error) {
|
||||||
masterKey := []byte(signingKey)
|
masterKey := []byte(signingKey)
|
||||||
|
|
||||||
// Derive separate keys for HMAC (hash) and encryption (block)
|
// Derive separate keys for HMAC (hash) and encryption (block)
|
||||||
@@ -62,8 +66,6 @@ func NewManager(signingKey string, secure bool) (*Manager, error) {
|
|||||||
|
|
||||||
return &Manager{
|
return &Manager{
|
||||||
sc: sc,
|
sc: sc,
|
||||||
secure: secure,
|
|
||||||
sameSite: http.SameSiteStrictMode,
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,8 +89,8 @@ func (m *Manager) CreateSession(w http.ResponseWriter) error {
|
|||||||
Path: "/",
|
Path: "/",
|
||||||
MaxAge: int(SessionTTL.Seconds()),
|
MaxAge: int(SessionTTL.Seconds()),
|
||||||
HttpOnly: true,
|
HttpOnly: true,
|
||||||
Secure: m.secure,
|
Secure: true,
|
||||||
SameSite: m.sameSite,
|
SameSite: http.SameSiteStrictMode,
|
||||||
})
|
})
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -131,8 +133,8 @@ func (m *Manager) ClearSession(w http.ResponseWriter) {
|
|||||||
Path: "/",
|
Path: "/",
|
||||||
MaxAge: -1, // Delete immediately
|
MaxAge: -1, // Delete immediately
|
||||||
HttpOnly: true,
|
HttpOnly: true,
|
||||||
Secure: m.secure,
|
Secure: true,
|
||||||
SameSite: m.sameSite,
|
SameSite: http.SameSiteStrictMode,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user