crowdsecurity/crowdsec · info

unknown cookie version

Error message

unknown cookie version

What it means

ErrCookieVersion signals that a presented cookie's first byte (version prefix) is not a version this build understands (only 0x00/v0 currently). openCookie dispatches on raw[0] and wraps the sentinel with the offending byte in hex. This lets the appsec layer respond with "epoch" (fresh challenge) rather than failing closed ambiguously, and leaves room for future cookie formats.

Source

Thrown at pkg/appsec/challenge/crypto.go:32

	"crypto/sha256"
	"encoding/base64"
	"encoding/binary"
	"errors"
	"fmt"
	"time"

	"golang.org/x/crypto/hkdf"

	"github.com/crowdsecurity/crowdsec/pkg/appsec/challenge/pb"
	"google.golang.org/protobuf/proto"
)

var (
	ErrCookieMalformed     = errors.New("malformed cookie")
	ErrCookieSignature     = errors.New("invalid cookie signature")
	ErrCookiePayload       = errors.New("invalid cookie payload")
	ErrCookieExpired       = errors.New("cookie expired")
	ErrCookieVersion       = errors.New("unknown cookie version")
	ErrAllowlistReasonSize = errors.New("allowlist reason exceeds maximum length")
	ErrCookieTooLarge      = errors.New("cookie exceeds maximum size")
)

const hkdfInfo = "crowdsec-challenge-cookie"

// MaxAllowlistReasonLen caps the reason string operators pass to
// GrantChallengeCookie. The reason travels inside every Set-Cookie + Cookie
// header round-trip until the cookie expires; bounding it keeps the cookie
// well under the 4 KB browser limit even with the AES-GCM tag + base64
// expansion.
const MaxAllowlistReasonLen = 256

// MaxCookieLen is the DEFAULT per-cookie size (RFC 6265 §6.1: 4096 bytes).
// Can be configured via Config.MaxCookieSize and we reject anything bigger.
const MaxCookieLen = 4096

// Cookie wire format. A single version byte at offset 0 lets us evolve the

View on GitHub (pinned to 909b515798)

Solutions

  1. Ensure all crowdsec/LAPI nodes run the same version so cookie formats match.
  2. Clear the client's cookie — the server responds with a fresh challenge ("epoch" remediation).
  3. If it reproduces from a freshly issued cookie, check for middleware/proxies mangling the Cookie header (encoding, length limits).
  4. Verify the master key / build is not from a divergent fork producing different cookie bytes.

Example fix

// before: opaque 500 on unrecognized cookie
resp := openCookie(raw)
// after: handle version mismatch with a fresh challenge
if errors.Is(err, challenge.ErrCookieVersion) {
    return issueFreshChallenge()
}
Defensive patterns

Strategy: try-catch

Validate before calling

// validate cookie shape before trusting it: version byte must be known
if len(raw) == 0 {
    return errors.New("empty cookie")
}
if raw[0] != 0x00 { // v0
    return fmt.Errorf("unsupported cookie version 0x%02x", raw[0])
}

Try / catch

if err := openCookie(raw, key, aad); err != nil {
    if errors.Is(err, challenge.ErrCookieVersion) {
        return issueFreshChallenge() // version skew: reissue
    }
    return err
}

Prevention

When it happens

Trigger: openCookie receives a cookie whose version byte is not 0; e.g. corrupted cookie, truncated value, or a cookie produced by a newer/older crowdsec with a different cookie format version. Returned by openCookie and asserted in TestCookie_UnknownVersionRejected.

Common situations: Version skew between rolling-updated crowdsec nodes sharing a master key; garbage appended to the cookie by a proxy; a user hand-editing or truncating the cookie value.

Related errors


AI-assisted analysis of crowdsecurity/crowdsec@909b515798 (2026-09-06). Data as JSON: /api/errors/b0b0328d7894ef07. Report an issue: GitHub.