Harden auth: setup secret, throttling, session destroy, secure cookies.
Replace username-based admin bootstrap with a one-time setup secret, rate-limit login/register, equalize login bcrypt timing, cap passwords at 72 bytes, destroy sessions on logout, and require Secure cookies when PORT is set.
This commit is contained in:
+30
-15
@@ -24,17 +24,24 @@ import (
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
AdminUsername string
|
||||
SecureCookie bool
|
||||
Blob blob.Uploader
|
||||
// AdminSetupSecret, when set, can promote the first registrant who also
|
||||
// posts the matching setup_secret. It is ignored once any admin exists.
|
||||
AdminSetupSecret string
|
||||
SecureCookie bool
|
||||
// TrustProxy enables X-Forwarded-For / RealIP only behind a known proxy.
|
||||
TrustProxy bool
|
||||
Blob blob.Uploader
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
store store.Store
|
||||
sessions *scs.SessionManager
|
||||
tmpl *template.Template
|
||||
cfg Config
|
||||
static http.Handler
|
||||
store store.Store
|
||||
sessions *scs.SessionManager
|
||||
tmpl *template.Template
|
||||
cfg Config
|
||||
static http.Handler
|
||||
loginIP *throttle
|
||||
loginUser *throttle
|
||||
registerIP *throttle
|
||||
}
|
||||
|
||||
type page struct {
|
||||
@@ -124,18 +131,23 @@ func New(st store.Store, sessionStore scs.Store, templateFS fs.FS, staticFS fs.F
|
||||
}
|
||||
|
||||
return &Server{
|
||||
store: st,
|
||||
sessions: sessions,
|
||||
tmpl: tmpl,
|
||||
cfg: cfg,
|
||||
static: http.StripPrefix("/static/", http.FileServer(http.FS(sub))),
|
||||
store: st,
|
||||
sessions: sessions,
|
||||
tmpl: tmpl,
|
||||
cfg: cfg,
|
||||
static: http.StripPrefix("/static/", http.FileServer(http.FS(sub))),
|
||||
loginIP: newThrottle(20, 15*time.Minute),
|
||||
loginUser: newThrottle(10, 15*time.Minute),
|
||||
registerIP: newThrottle(10, 15*time.Minute),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) Handler() http.Handler {
|
||||
r := chi.NewRouter()
|
||||
r.Use(middleware.RequestID)
|
||||
r.Use(middleware.RealIP)
|
||||
if s.cfg.TrustProxy {
|
||||
r.Use(middleware.RealIP)
|
||||
}
|
||||
r.Use(middleware.Logger)
|
||||
r.Use(middleware.Recoverer)
|
||||
r.Use(func(next http.Handler) http.Handler {
|
||||
@@ -507,7 +519,10 @@ func (s *Server) handleLogout(w http.ResponseWriter, r *http.Request) {
|
||||
if !s.requireCSRF(w, r) {
|
||||
return
|
||||
}
|
||||
s.sessions.Remove(r.Context(), "user_id")
|
||||
if err := s.sessions.Destroy(r.Context()); err != nil {
|
||||
http.Error(w, "could not sign out", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user