securecookie

package module
v1.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 7, 2026 License: MIT Imports: 10 Imported by: 1

README

securecookie

Go Reference

securecookie encodes and decodes authenticated and encrypted cookie values.

It is a modernized fork of gorilla/securecookie: instead of HMAC signing with optional AES-CTR encryption, cookie values are always sealed with the XChaCha20-Poly1305 AEAD, with the cookie name bound as additional authenticated data and the issue timestamp encrypted alongside the payload.

Install

go get github.com/libtnb/securecookie

Usage

Keys must be exactly 32 bytes. Generate one once and persist it — a new key on every restart invalidates all previously issued cookies.

key := securecookie.GenerateRandomKey(32)

s, err := securecookie.New(key, nil) // nil means securecookie.DefaultOptions
if err != nil {
    log.Fatal(err)
}

// Encode.
encoded, err := s.Encode("session", map[string]string{"user": "42"})

// Decode. Returns the timestamp at which the cookie was encoded.
value := map[string]string{}
ts, err := s.Decode("session", encoded, &value)
Options
s, err := securecookie.New(key, &securecookie.Options{
    RotatedKeys: [][]byte{oldKey}, // previous keys, tried when decoding
    MinAge:      0,                // reject cookies newer than this, in seconds (0 = off)
    MaxAge:      86400 * 30,       // reject cookies older than this, in seconds (0 = off)
    MaxLength:   4096,             // maximum encoded length, in bytes (0 = off)
    Serializer:  securecookie.JSONEncoder{}, // or GobEncoder{} / NopEncoder{}
})
Key rotation

Pass previous keys in RotatedKeys to keep already-issued cookies valid after switching to a new primary key. Rotated keys are only used for decoding — new cookies are always encrypted with the primary key.

Security model

  • Confidentiality and integrity via XChaCha20-Poly1305 with a random 24-byte nonce per cookie.
  • The cookie name is authenticated as additional data, so a value cannot be replayed under a different cookie name.
  • The issue timestamp is sealed inside the ciphertext and checked against MinAge/MaxAge before the payload is deserialized.

License

BSD licensed, see LICENSE.

Documentation

Overview

Package securecookie encodes and decodes authenticated and encrypted cookie values.

Cookie values are serialized (JSON by default), sealed together with a timestamp using XChaCha20-Poly1305, and base64-encoded. The cookie name is bound to the ciphertext as additional authenticated data, so a value issued for one cookie name cannot be replayed under another. The timestamp is validated against MinAge/MaxAge before the payload is deserialized.

Basic usage:

key := securecookie.GenerateRandomKey(32) // persist this somewhere!
s, err := securecookie.New(key, nil)
if err != nil {
	// ...
}

// Encode a value into a cookie.
encoded, err := s.Encode("session", map[string]string{"user": "42"})

// Decode it back.
value := map[string]string{}
ts, err := s.Decode("session", encoded, &value)

To rotate keys, pass the previous keys in Options.RotatedKeys. New cookies are always encrypted with the primary key; rotated keys are only tried when decoding, so cookies issued before the rotation stay valid until they expire:

s, err := securecookie.New(newKey, &securecookie.Options{
	RotatedKeys: [][]byte{oldKey},
})

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrKeyLength        = fmt.Errorf("the key must be %d bytes", chacha20poly1305.KeySize)
	ErrDecryptionFailed = fmt.Errorf("the value could not be decrypted")
	ErrValueNotByte     = fmt.Errorf("the value is not a []byte")
	ErrValueNotBytePtr  = fmt.Errorf("the value is not a *[]byte")
	ErrValueTooLong     = fmt.Errorf("the value is too long")
	ErrTimestampInvalid = fmt.Errorf("the timestamp is invalid")
	ErrTimestampTooNew  = fmt.Errorf("the timestamp is too new")
	ErrTimestampExpired = fmt.Errorf("the timestamp is expired")
)
View Source
var DefaultOptions = &Options{
	MinAge:     0,
	MaxAge:     86400 * 30,
	MaxLength:  4096,
	Serializer: JSONEncoder{},
	TimeFunc:   defaultTimeFunc,
}

Functions

func GenerateRandomKey

func GenerateRandomKey(length int) []byte

GenerateRandomKey creates a random key with the given length in bytes. It panics if the system's secure random number source fails, in which case the process should not continue.

Note that keys created using `GenerateRandomKey()` are not automatically persisted. New keys will be created when the application is restarted, and previously issued cookies will not be able to be decoded.

Types

type Codec

type Codec interface {
	Encode(name string, value any) (string, error)
	Decode(name, value string, dst any) (int64, error)
}

Codec defines an interface to encode and decode cookie values.

type GobEncoder

type GobEncoder struct{}

GobEncoder encodes cookie values using encoding/gob. This is the simplest encoder and can handle complex types via gob.Register.

func (GobEncoder) Deserialize

func (e GobEncoder) Deserialize(src []byte, dst any) error

Deserialize decodes a value using gob.

func (GobEncoder) Serialize

func (e GobEncoder) Serialize(src any) ([]byte, error)

Serialize encodes a value using gob.

type JSONEncoder

type JSONEncoder struct{}

JSONEncoder encodes cookie values using encoding/json. Users who wish to encode complex types need to satisfy the json.Marshaller and json.Unmarshaller interfaces.

func (JSONEncoder) Deserialize

func (e JSONEncoder) Deserialize(src []byte, dst any) error

Deserialize decodes a value using encoding/json.

func (JSONEncoder) Serialize

func (e JSONEncoder) Serialize(src any) ([]byte, error)

Serialize encodes a value using encoding/json.

type NopEncoder

type NopEncoder struct{}

NopEncoder does not encode cookie values, and instead simply accepts a []byte (as an any) and returns a []byte. This is particularly useful when you encoding an object upstream and do not wish to re-encode it.

func (NopEncoder) Deserialize

func (e NopEncoder) Deserialize(src []byte, dst any) error

Deserialize passes a []byte through as-is.

func (NopEncoder) Serialize

func (e NopEncoder) Serialize(src any) ([]byte, error)

Serialize passes a []byte through as-is.

type Options

type Options struct {
	// RotatedKeys holds previous keys, tried in order when decoding so that
	// cookies issued before a key rotation remain valid. They are never used
	// for encoding. Each key must be 32 bytes.
	RotatedKeys [][]byte
	// MinAge is the minimum age of a cookie, in seconds. Cookies encoded
	// less than MinAge seconds ago are rejected. 0 disables the check.
	MinAge int64
	// MaxAge is the maximum age of a cookie, in seconds. Cookies encoded
	// more than MaxAge seconds ago are rejected. 0 disables the check.
	MaxAge int64
	// MaxLength is the maximum length of an encoded cookie value, in bytes.
	// 0 disables the check. Note that browsers commonly limit the whole
	// cookie (name, value and attributes) to 4096 bytes.
	MaxLength int
	// Serializer converts values to and from bytes. Defaults to JSONEncoder.
	Serializer Serializer
	// TimeFunc returns the current Unix timestamp, in seconds. It exists so
	// tests can supply a fake clock. Defaults to time.Now().UTC().Unix().
	TimeFunc func() int64
}

type SecureCookie

type SecureCookie struct {
	// contains filtered or unexported fields
}

SecureCookie encodes and decodes authenticated and encrypted cookie values. It is safe for concurrent use by multiple goroutines, as long as the configured Serializer and TimeFunc are (the defaults are).

func New

func New(key []byte, options *Options) (*SecureCookie, error)

New returns a new SecureCookie.

Key is required and must be 32 bytes, used to authenticate and encrypt cookie values. The same length requirement applies to every key in options.RotatedKeys.

If options is nil, DefaultOptions is used. The provided Options value is only read, never modified.

Note that keys created using GenerateRandomKey() are not automatically persisted. New keys will be created when the application is restarted, and previously issued cookies will not be able to be decoded.

func (*SecureCookie) Decode

func (s *SecureCookie) Decode(name, value string, dst any) (int64, error)

Decode decodes a cookie value.

It decodes the base64 value, decrypts and verifies the ciphertext, checks the embedded timestamp against MinAge/MaxAge, and finally deserializes the value into dst, which must be a pointer.

The name argument is the cookie name. It must be the same name used when it was encoded.

It returns the timestamp at which the cookie was encoded whenever it is known, even if a timestamp or deserialization error is returned alongside.

func (*SecureCookie) Encode

func (s *SecureCookie) Encode(name string, value any) (string, error)

Encode encodes a cookie value.

It serializes the value, encrypts and authenticates it together with a timestamp using the primary key, and finally encodes the result with base64. Rotated keys are never used for encoding.

The name argument is the cookie name. It is bound to the ciphertext as additional authenticated data, so a value issued for one cookie name cannot be replayed under another.

type Serializer

type Serializer interface {
	Serialize(src any) ([]byte, error)
	Deserialize(src []byte, dst any) error
}

Serializer provides an interface for providing custom serializers for cookie values.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL