package security import ( "bytes" "encoding/base64" "encoding/gob" "fmt" "net/http" "os" "time" "github.com/gorilla/securecookie" "github.com/gorilla/sessions" ) var ( sessionStore *sessions.CookieStore sessionName string authKey []byte encryptKey []byte ) func init() { gob.Register(time.Time{}) } func LoadSessionKeys(authPath, encryptionPath, name string, isProduction bool) error { var err error rawAuth, err := os.ReadFile(authPath) if err != nil { return fmt.Errorf("error reading auth key: %w", err) } authKey, err = base64.StdEncoding.DecodeString(string(bytes.TrimSpace(rawAuth))) if err != nil { return fmt.Errorf("error decoding auth key: %w", err) } rawEnc, err := os.ReadFile(encryptionPath) if err != nil { return fmt.Errorf("error reading encryption key: %w", err) } encryptKey, err = base64.StdEncoding.DecodeString(string(bytes.TrimSpace(rawEnc))) if err != nil { return fmt.Errorf("error decoding encryption key: %w", err) } if len(authKey) != 32 || len(encryptKey) != 32 { return fmt.Errorf("auth and encryption keys must be 32 bytes each (got auth=%d, enc=%d)", len(authKey), len(encryptKey)) } sessionStore = sessions.NewCookieStore(authKey, encryptKey) sessionStore.Options = &sessions.Options{ Path: "/", MaxAge: 86400 * 1, HttpOnly: true, Secure: isProduction, SameSite: http.SameSiteLaxMode, } sessionName = name return nil } func GetSession(w http.ResponseWriter, r *http.Request) (*sessions.Session, error) { return sessionStore.Get(r, sessionName) } func SecureCookie(w http.ResponseWriter, name, value string, isProduction bool) error { s := securecookie.New(authKey, encryptKey) encoded, err := s.Encode(name, value) if err != nil { return err } http.SetCookie(w, &http.Cookie{ Name: name, Value: encoded, Path: "/", HttpOnly: true, Secure: isProduction, SameSite: http.SameSiteStrictMode, }) return nil } func LoadKeyFromFile(path string) ([]byte, error) { key, err := os.ReadFile(path) if err != nil { return nil, err } return bytes.TrimSpace(key), nil } func ZeroBytes(b []byte) { for i := range b { b[i] = 0 } }