Documentation
¶
Overview ¶
Package securecookie provides authenticated cookie values in two modes:
SecureCookie -- AES-GCM authenticated encryption: payload is secret and tamper-proof. Use for sensitive data (PII, credentials, tokens). Three key sizes: 16 (AES-128), 24 (AES-192), or 32 (AES-256) bytes.
SignedCookie -- HMAC-SHA256 authenticated signing: payload is readable but tamper-proof. Use when the payload is already opaque (JWTs, server-issued opaque IDs) or non-sensitive. Saves the AES key schedule and avoids double-encrypting opaque data. Any non-empty key is accepted; 32 bytes is recommended.
Basic Usage ¶
key, _ := securecookie.GenerateKey(32)
sc, _ := securecookie.New(key)
encoded, _ := sc.Encode(map[string]string{"user": "alice"})
var dst map[string]string
_ = sc.Decode(encoded, &dst)
Configuration ¶
Builder-style methods configure cookie validation:
sc, _ := securecookie.New(key) sc.MaxAge(3600). // reject cookies older than 1 hour MinAge(10). // reject cookies younger than 10 seconds MaxLength(8192) // max encoded value length in bytes
Signed-Only Mode ¶
NewSigned creates a SignedCookie for HMAC-SHA256 signing without encryption. Same fluent API and same Codec interface as SecureCookie:
key, _ := securecookie.GenerateSignedKey(32)
sc, _ := securecookie.NewSigned(key)
encoded, _ := sc.Encode("eyJhbGciOiJ...") // a JWT
var dst string
_ = sc.Decode(encoded, &dst)
Key Rotation ¶
CodecsFromKeys (encrypted) and SignedCodecsFromKeys (signed) create a Codec slice from multiple keys. EncodeMulti always encodes with the first (newest) key. DecodeMulti tries each key in order until one succeeds, enabling seamless rotation without invalidating existing cookies:
codecs, _ := securecookie.CodecsFromKeys(currentKey, previousKey) encoded, _ := securecookie.EncodeMulti(value, codecs...) _ = securecookie.DecodeMulti(encoded, &dst, codecs...)
During rotation, add the new key at the front of the list and keep old keys until all cookies issued with them have expired (MaxAge).
Additional Authenticated Data (AAD) ¶
By default no AAD is used. Use SecureCookie.AdditionalData to bind cookies to context such as a user ID, cookie name, or tenant:
sc.AdditionalData([]byte("user-123")) // bind to user
sc.AdditionalData([]byte("session")) // bind to cookie name
sc.AdditionalData(nil) // clear AAD (default)
Compression ¶
Payloads are automatically compressed with deflate before encryption when beneficial. A Shannon entropy check skips compression for high-entropy data (> 6.5 bits/byte) and small payloads (< 32 bytes) to avoid wasted CPU. This is transparent and requires no configuration.
Timestamp Validation ¶
Each encoded value embeds a Unix timestamp. On decode, the timestamp is checked against configurable MaxAge and MinAge bounds to enforce cookie freshness. Cookies with future timestamps beyond 5 minutes of clock skew are always rejected.
Serialization ¶
Values are serialized to JSON by default. A custom Serializer can be provided via SecureCookie.SetSerializer:
sc.SetSerializer(mySerializer{})
Security Considerations ¶
- Always use cryptographically random keys (see GenerateKey).
- Prefer 32-byte keys (AES-256) for maximum security margin.
- Always transmit cookies over HTTPS.
- Bind cookies with HttpOnly, Secure, and SameSite attributes at the HTTP layer; this package handles only the value encoding.
- Decompressed payloads are limited to 512 KB to prevent zip-bomb attacks.
References:
- NIST SP 800-38D: Recommendation for Block Cipher Modes of Operation: GCM
- RFC 6265: HTTP State Management Mechanism
Index ¶
- Constants
- Variables
- func DecodeMulti(value string, dst any, codecs ...Codec) error
- func EncodeMulti(value any, codecs ...Codec) (string, error)
- func GenerateKey(size int) ([]byte, error)
- func GenerateSignedKey(size int) ([]byte, error)
- type Codec
- type JSONSerializer
- type SecureCookie
- func (s *SecureCookie) AdditionalData(data []byte) *SecureCookie
- func (s *SecureCookie) Decode(value string, dst any) error
- func (s *SecureCookie) Encode(value any) (string, error)
- func (s *SecureCookie) MaxAge(seconds int) *SecureCookie
- func (s *SecureCookie) MaxLength(length int) *SecureCookie
- func (s *SecureCookie) MinAge(seconds int) *SecureCookie
- func (s *SecureCookie) SetSerializer(sz Serializer) *SecureCookie
- type Serializer
- type SignedCookie
- func (s *SignedCookie) AdditionalData(data []byte) *SignedCookie
- func (s *SignedCookie) Decode(value string, dst any) error
- func (s *SignedCookie) Encode(value any) (string, error)
- func (s *SignedCookie) MaxAge(seconds int) *SignedCookie
- func (s *SignedCookie) MaxLength(length int) *SignedCookie
- func (s *SignedCookie) MinAge(seconds int) *SignedCookie
- func (s *SignedCookie) SetSerializer(sz Serializer) *SignedCookie
Constants ¶
const ( // DefaultMaxAge is the default maximum cookie age: 30 days. DefaultMaxAge = 30 * 24 * 60 * 60 // DefaultMaxLength is the default maximum encoded cookie length in bytes. DefaultMaxLength = 4096 )
Variables ¶
var ( // ErrInvalidKey is returned when the encryption key is not 16, 24, or 32 bytes. ErrInvalidKey = errors.New("securecookie: invalid key (must be 16, 24, or 32 bytes)") // ErrEncodeFailed is returned when encoding a cookie value fails. ErrEncodeFailed = errors.New("securecookie: encode failed") // ErrDecodeFailed is returned when decoding a cookie value fails. ErrDecodeFailed = errors.New("securecookie: decode failed") // ErrValueTooLong is returned when the encoded value exceeds MaxLength. ErrValueTooLong = errors.New("securecookie: encoded value too long") // ErrTimestampExpired is returned when the cookie timestamp exceeds MaxAge. ErrTimestampExpired = errors.New("securecookie: cookie expired") // ErrTimestampTooNew is returned when the cookie timestamp is newer than MinAge allows. ErrTimestampTooNew = errors.New("securecookie: cookie too new") // ErrTimestampFuture is returned when the cookie timestamp is in the future. ErrTimestampFuture = errors.New("securecookie: cookie timestamp is in the future") // ErrNoCodecs is returned when an empty codec list is passed to EncodeMulti or DecodeMulti. ErrNoCodecs = errors.New("securecookie: no codecs provided") )
Encoding and decoding errors.
Functions ¶
func DecodeMulti ¶
DecodeMulti tries each codec in order and returns the first successful decode. Returns the last error if all codecs fail.
For SecureCookie codecs (produced by CodecsFromKeys), dst is only written on a fully successful decode because GCM decryption and timestamp validation occur before deserialization. Custom Codec implementations should follow the same pattern: validate before writing to dst.
func EncodeMulti ¶
EncodeMulti encodes a value using the first codec in the list.
func GenerateKey ¶
GenerateKey returns a cryptographically random key suitable for use with New. The size must be 16 (AES-128), 24 (AES-192), or 32 (AES-256).
Types ¶
type Codec ¶
Codec encodes and decodes cookie values.
func CodecsFromKeys ¶
CodecsFromKeys creates a Codec slice from one or more AES keys. Each key must be 16, 24, or 32 bytes. The first key is used for encoding; all keys are tried for decoding.
func SignedCodecsFromKeys ¶ added in v0.13.0
SignedCodecsFromKeys returns a slice of Codec backed by SignedCookie instances, one per key, for graceful key rotation. The first key is used for new cookies; all keys are tried for decoding.
type JSONSerializer ¶
type JSONSerializer struct{}
JSONSerializer uses JSON encoding. This is the default serializer.
Note: encoding/json replaces invalid UTF-8 in string fields with the Unicode replacement character (U+FFFD). Use []byte fields for binary data that must round-trip exactly.
func (JSONSerializer) Deserialize ¶
func (JSONSerializer) Deserialize(src []byte, dst any) error
Deserialize unmarshals JSON bytes into dst.
type SecureCookie ¶
type SecureCookie struct {
// contains filtered or unexported fields
}
SecureCookie encodes and decodes authenticated, encrypted cookie values using AES-GCM (AES-128, AES-192, or AES-256 depending on key size).
func New ¶
func New(key []byte) (*SecureCookie, error)
New creates a SecureCookie with the given AES key. The key must be 16 bytes (AES-128), 24 bytes (AES-192), or 32 bytes (AES-256).
func (*SecureCookie) AdditionalData ¶
func (s *SecureCookie) AdditionalData(data []byte) *SecureCookie
AdditionalData sets custom additional authenticated data (AAD) bound into the GCM authentication tag. Use this to bind cookies to context such as a user ID, session ID, or cookie name.
By default no AAD is used. Pass nil to clear.
func (*SecureCookie) Decode ¶
func (s *SecureCookie) Decode(value string, dst any) error
Decode base64-decodes, decrypts, validates the timestamp, and deserializes a cookie value into dst.
func (*SecureCookie) Encode ¶
func (s *SecureCookie) Encode(value any) (string, error)
Encode serializes, encrypts, and base64-encodes a value.
func (*SecureCookie) MaxAge ¶
func (s *SecureCookie) MaxAge(seconds int) *SecureCookie
MaxAge sets the maximum age in seconds. Cookies older than this are rejected during decode. Set to 0 to disable age checking. Negative values are treated as 0.
func (*SecureCookie) MaxLength ¶
func (s *SecureCookie) MaxLength(length int) *SecureCookie
MaxLength sets the maximum encoded cookie value length in bytes. Set to 0 to disable length checking. Negative values are treated as 0.
func (*SecureCookie) MinAge ¶
func (s *SecureCookie) MinAge(seconds int) *SecureCookie
MinAge sets the minimum age in seconds. Cookies newer than this are rejected during decode. Default: 0 (no minimum). Negative values are treated as 0.
func (*SecureCookie) SetSerializer ¶
func (s *SecureCookie) SetSerializer(sz Serializer) *SecureCookie
SetSerializer sets the serializer used for encoding and decoding values. A nil serializer is ignored. Default: JSONSerializer.
type Serializer ¶
type Serializer interface {
Serialize(src any) ([]byte, error)
Deserialize(src []byte, dst any) error
}
Serializer defines how values are converted to and from bytes for storage in a cookie.
type SignedCookie ¶ added in v0.13.0
type SignedCookie struct {
// contains filtered or unexported fields
}
SignedCookie encodes and decodes authenticated (HMAC-SHA256) but unencrypted cookie values. The payload is integrity-protected against tampering but readable to anyone who can read the cookie.
Use SignedCookie when the cookie payload is already opaque or non-sensitive: JWTs, OAuth state, opaque server-issued IDs, or anti-CSRF tokens. Prefer SecureCookie for any data that must remain secret from the client.
SignedCookie avoids the AES key schedule on every request and uses a 32-byte HMAC tag instead of a 12-byte nonce + 16-byte auth tag, which can give smaller cookies for some payloads after compression.
func NewSigned ¶ added in v0.13.0
func NewSigned(key []byte) (*SignedCookie, error)
NewSigned creates a SignedCookie with the given HMAC-SHA256 key. Any non-empty key is accepted; 32 bytes is recommended (RFC 2104 §3). Use GenerateSignedKey to produce a fresh key.
func (*SignedCookie) AdditionalData ¶ added in v0.13.0
func (s *SignedCookie) AdditionalData(data []byte) *SignedCookie
AdditionalData sets custom additional authenticated data bound into the HMAC. Use this to namespace cookies that share a key (e.g., separate "session" from "csrf" tokens) and to bind cookies to context such as a user ID. Cookies signed with one AAD will not verify under another.
By default no AAD is used. Pass nil to clear.
func (*SignedCookie) Decode ¶ added in v0.13.0
func (s *SignedCookie) Decode(value string, dst any) error
Decode base64-decodes, verifies the HMAC, validates the timestamp, and deserializes a cookie value into dst.
func (*SignedCookie) Encode ¶ added in v0.13.0
func (s *SignedCookie) Encode(value any) (string, error)
Encode serializes, signs, and base64-encodes a value. The wire format is base64url(timestamp || payload || hmac-sha256(timestamp || payload || aad)).
func (*SignedCookie) MaxAge ¶ added in v0.13.0
func (s *SignedCookie) MaxAge(seconds int) *SignedCookie
MaxAge sets the maximum age in seconds. Cookies older than this are rejected during decode. Set to 0 to disable age checking. Negative values are treated as 0.
func (*SignedCookie) MaxLength ¶ added in v0.13.0
func (s *SignedCookie) MaxLength(length int) *SignedCookie
MaxLength sets the maximum encoded cookie value length in bytes. Set to 0 to disable length checking. Negative values are treated as 0.
func (*SignedCookie) MinAge ¶ added in v0.13.0
func (s *SignedCookie) MinAge(seconds int) *SignedCookie
MinAge sets the minimum age in seconds. Cookies newer than this are rejected during decode. Default: 0 (no minimum). Negative values are treated as 0.
func (*SignedCookie) SetSerializer ¶ added in v0.13.0
func (s *SignedCookie) SetSerializer(sz Serializer) *SignedCookie
SetSerializer sets the serializer used for encoding and decoding values. A nil serializer is ignored. Default: JSONSerializer.