fix: encrypt the cookie in sessions

This commit is contained in:
Stavros
2025-07-04 01:08:17 +03:00
parent 7640e956c2
commit ebcf6e6aa6
4 changed files with 61 additions and 5 deletions

View File

@@ -74,6 +74,15 @@ var rootCmd = &cobra.Command{
csrfCookieName := fmt.Sprintf("%s-%s", constants.CsrfCookieName, cookieId) csrfCookieName := fmt.Sprintf("%s-%s", constants.CsrfCookieName, cookieId)
redirectCookieName := fmt.Sprintf("%s-%s", constants.RedirectCookieName, cookieId) redirectCookieName := fmt.Sprintf("%s-%s", constants.RedirectCookieName, cookieId)
// Generate HMAC and encryption secrets
log.Debug().Msg("Deriving HMAC and encryption secrets")
hmacSecret, err := utils.DeriveKey(config.Secret, "hmac")
HandleError(err, "Failed to derive HMAC secret")
encryptionSecret, err := utils.DeriveKey(config.Secret, "encryption")
HandleError(err, "Failed to derive encryption secret")
// Create OAuth config // Create OAuth config
oauthConfig := types.OAuthConfig{ oauthConfig := types.OAuthConfig{
GithubClientId: config.GithubClientId, GithubClientId: config.GithubClientId,
@@ -115,13 +124,14 @@ var rootCmd = &cobra.Command{
authConfig := types.AuthConfig{ authConfig := types.AuthConfig{
Users: users, Users: users,
OauthWhitelist: config.OAuthWhitelist, OauthWhitelist: config.OAuthWhitelist,
Secret: config.Secret,
CookieSecure: config.CookieSecure, CookieSecure: config.CookieSecure,
SessionExpiry: config.SessionExpiry, SessionExpiry: config.SessionExpiry,
Domain: domain, Domain: domain,
LoginTimeout: config.LoginTimeout, LoginTimeout: config.LoginTimeout,
LoginMaxRetries: config.LoginMaxRetries, LoginMaxRetries: config.LoginMaxRetries,
SessionCookieName: sessionCookieName, SessionCookieName: sessionCookieName,
HMACSecret: hmacSecret,
EncryptionSecret: encryptionSecret,
} }
// Create hooks config // Create hooks config

View File

@@ -33,7 +33,7 @@ type Auth struct {
func (auth *Auth) GetSession(c *gin.Context) (*sessions.Session, error) { func (auth *Auth) GetSession(c *gin.Context) (*sessions.Session, error) {
// Create cookie store // Create cookie store
store := sessions.NewCookieStore([]byte(auth.Config.Secret)) store := sessions.NewCookieStore([]byte(auth.Config.HMACSecret), []byte(auth.Config.EncryptionSecret))
// Configure cookie store // Configure cookie store
store.Options = &sessions.Options{ store.Options = &sessions.Options{
@@ -46,9 +46,21 @@ func (auth *Auth) GetSession(c *gin.Context) (*sessions.Session, error) {
// Get session // Get session
session, err := store.Get(c.Request, auth.Config.SessionCookieName) session, err := store.Get(c.Request, auth.Config.SessionCookieName)
if err != nil { if err != nil {
log.Error().Err(err).Msg("Failed to get session") log.Warn().Err(err).Msg("Invalid session, clearing cookie and retrying")
return nil, err
// Delete the session cookie if there is an error
c.SetCookie(auth.Config.SessionCookieName, "", -1, "/", auth.Config.Domain, auth.Config.CookieSecure, true)
// Try to get the session again
session, err = store.Get(c.Request, auth.Config.SessionCookieName)
if err != nil {
// If we still can't get the session, log the error and return nil
log.Error().Err(err).Msg("Failed to get session")
return nil, err
}
} }
return session, nil return session, nil

View File

@@ -80,12 +80,13 @@ type AuthConfig struct {
Users Users Users Users
OauthWhitelist string OauthWhitelist string
SessionExpiry int SessionExpiry int
Secret string
CookieSecure bool CookieSecure bool
Domain string Domain string
LoginTimeout int LoginTimeout int
LoginMaxRetries int LoginMaxRetries int
SessionCookieName string SessionCookieName string
HMACSecret string
EncryptionSecret string
} }
// HooksConfig is the configuration for the hooks service // HooksConfig is the configuration for the hooks service

View File

@@ -1,8 +1,11 @@
package utils package utils
import ( import (
"bytes"
"crypto/sha256"
"encoding/base64" "encoding/base64"
"errors" "errors"
"io"
"net" "net"
"net/url" "net/url"
"os" "os"
@@ -11,6 +14,7 @@ import (
"tinyauth/internal/types" "tinyauth/internal/types"
"github.com/traefik/paerser/parser" "github.com/traefik/paerser/parser"
"golang.org/x/crypto/hkdf"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
@@ -405,3 +409,32 @@ func FilterIP(filter string, ip string) (bool, error) {
// If the filter is not a CIDR range or a single IP, return false // If the filter is not a CIDR range or a single IP, return false
return false, nil return false, nil
} }
func DeriveKey(secret string, info string) (string, error) {
// Create hashing function
hash := sha256.New
// Create a new key using the secret and info
hkdf := hkdf.New(hash, []byte(secret), nil, []byte(info)) // I am not using a salt because I just want two different keys from one secret, maybe bad practice
// Create a new key
key := make([]byte, 32)
// Read the key from the HKDF
_, err := io.ReadFull(hkdf, key)
if err != nil {
return "", err
}
// Verify the key is not empty
if bytes.Equal(key, make([]byte, 32)) {
return "", errors.New("derived key is empty")
}
// Encode the key to base64
encodedKey := base64.StdEncoding.EncodeToString(key)
// Return the key as a base64 encoded string
return encodedKey[:32], nil
}