crowdsecurity/crowdsec · info
cookie expired
Error message
cookie expired
What it means
ErrCookieExpired is a sentinel error in the challenge cookie crypto layer indicating that a challenge cookie's not_after timestamp has passed. openCookieV0Bytes compares the cookie's embedded not_after value against the current wall clock and wraps this sentinel with the concrete timestamps. appsec.go maps it to the "epoch" remediation so the client gets a fresh challenge instead of an opaque 500.
Source
Thrown at pkg/appsec/challenge/crypto.go:31
"crypto/rand"
"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
View on GitHub (pinned to 909b515798)
Solutions
- Let the client re-solve the challenge: the "epoch" remediation already issues a fresh cookie, so treat this as expected flow, not a bug.
- Check for clock skew (NTP) if valid users are systematically rejected.
- If widespread, verify the cookie TTL configuration is not set to an unreasonably small value.
- Ensure clients are not caching/persisting cookies beyond their TTL (e.g. proxies replaying Set-Cookie).
Example fix
// before: treating any cookie error as fatal
if err := openCookie(raw, key, aad); err != nil {
return fmt.Errorf("cookie rejected: %w", err)
}
// after: branch on expiry and reissue
if errors.Is(err, challenge.ErrCookieExpired) {
return issueFreshChallenge() // "epoch" remediation
} Defensive patterns
Strategy: try-catch
Validate before calling
// Go: no pre-check of expiry is possible without opening the cookie; // keep cookie TTL comfortably above expected session length const cookieTTL = 10 * time.Minute
Try / catch
if err := openCookie(raw, key, aad); err != nil {
switch {
case errors.Is(err, challenge.ErrCookieExpired):
// expected: reissue via "epoch" remediation
return issueFreshChallenge()
default:
return err
}
} Prevention
- Set cookie TTL generously relative to user session length
- Sync clocks (NTP) across all crowdsec nodes
- Treat ErrCookieExpired as normal flow and reissue, never as a 5xx
- Monitor expiry rates to catch TTL misconfiguration
When it happens
Trigger: A client presents a Cookie whose embedded not_after <= time.Now().Unix() during openCookieV0Bytes; also asserted in tests TestCookieV0_ExpiredRejected and TestCookieV0_ExpiryEnforcedAgainstWallClock.
Common situations: A user keeps a browser tab open past the cookie TTL and retries; a clock skew between issuing and validating nodes; replaying an old captured cookie; stale cookies after the master key rotation left them unrotated.
Related errors
- unknown cookie version
- failed to seal challenge cookie: %w
- failed to seal allowlist cookie: %w
- invalid challenge cookie: %w
- ErrCookieExpired
AI-assisted analysis of crowdsecurity/crowdsec@909b515798 (2026-09-06).
Data as JSON: /api/errors/effb3d0e7caed339.
Report an issue: GitHub.