From f0fbdab72254d68d0a3a4a49a4a1646f89f0f913 Mon Sep 17 00:00:00 2001 From: mo khan Date: Wed, 7 May 2025 10:30:59 -0700 Subject: feat: digitally sign and verify cookie using randomly generated key --- pkg/web/cookie.go | 44 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) (limited to 'pkg') diff --git a/pkg/web/cookie.go b/pkg/web/cookie.go index 1377274..4795f96 100644 --- a/pkg/web/cookie.go +++ b/pkg/web/cookie.go @@ -1,18 +1,28 @@ package web import ( + "crypto/sha256" + "encoding/base64" + "fmt" "net/http" + "strings" "github.com/xlgmokha/x/pkg/cookie" + "github.com/xlgmokha/x/pkg/crypt" "github.com/xlgmokha/x/pkg/env" + "github.com/xlgmokha/x/pkg/pls" "github.com/xlgmokha/x/pkg/x" ) +var key []byte = x.Must(pls.GenerateRandomBytes(32)) // TODO:: Read fix key from environment variable +var Signer *crypt.HMACSigner = x.New[*crypt.HMACSigner](crypt.WithKey(key), crypt.WithAlgorithm(sha256.New)) +var delimiter string = "--" + func NewCookie(name, value string, options ...x.Option[*http.Cookie]) *http.Cookie { return x.New[*http.Cookie](x.Prepend[x.Option[*http.Cookie]]( options, cookie.WithName(name), - cookie.WithValue(value), // TODO:: digitally sign the value + withSignedValue(value), cookie.WithPath("/"), cookie.WithHttpOnly(true), cookie.WithSecure(true), @@ -21,6 +31,19 @@ func NewCookie(name, value string, options ...x.Option[*http.Cookie]) *http.Cook )...) } +func withSignedValue(value string) x.Option[*http.Cookie] { + signature, err := Signer.Sign([]byte(value)) + if err != nil { + return nil + } + return cookie.WithValue(fmt.Sprintf( + "%v%v%v", + value, + delimiter, + base64.URLEncoding.EncodeToString(signature), + )) +} + func ExpireCookie(w http.ResponseWriter, name string) { cookie.Expire(w, name, cookie.WithPath("/"), @@ -29,3 +52,22 @@ func ExpireCookie(w http.ResponseWriter, name string) { cookie.WithSecure(true), ) } + +func CookieValueFrom(c *http.Cookie) string { + segments := strings.SplitN(c.Value, delimiter, 2) + if len(segments) != 2 { + return "" + } + + data := segments[0] + signature, err := base64.URLEncoding.DecodeString(segments[1]) + if err != nil { + return "" + } + + if !Signer.Verify([]byte(data), []byte(signature)) { + return "" + } + + return data +} -- cgit v1.2.3