crowdsecurity/crowdsec · error

ErrCookiePayload

ErrCookiePayload

Error message

%w: %w

What it means

After extracting the allowlist reason, openCookieV0Bytes unmarshals the remaining plaintext bytes as a pb.ChallengeCookie protobuf. If proto.Unmarshal fails, the tail of the plaintext is not a valid ChallengeCookie message and the error is wrapped in ErrCookiePayload. This indicates an incompatible, truncated, or forged cookie payload.

Source

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

	if reasonLen > MaxAllowlistReasonLen {
		return nil, fmt.Errorf("%w: reason_len=%d", ErrCookieMalformed, reasonLen)
	}

	if len(plaintext) < cookiePlaintextFixedHeaderLen+reasonLen {
		return nil, fmt.Errorf("%w: plaintext shorter than declared reason_len", ErrCookieMalformed)
	}

	if notAfter <= now.Unix() {
		return nil, fmt.Errorf("%w: not_after=%d now=%d", ErrCookieExpired, notAfter, now.Unix())
	}

	reasonStart := cookiePlaintextFixedHeaderLen
	reasonEnd := reasonStart + reasonLen
	reason := string(plaintext[reasonStart:reasonEnd])

	envelope := &pb.ChallengeCookie{}
	if err := proto.Unmarshal(plaintext[reasonEnd:], envelope); err != nil {
		return nil, fmt.Errorf("%w: %w", ErrCookiePayload, err)
	}

	return &CookieEnvelope{
		Envelope:        envelope,
		Allowlisted:     flags&cookieFlagAllowlisted != 0,
		AllowlistReason: reason,
		NotAfter:        notAfter,
	}, nil
}

View on GitHub (pinned to 909b515798)

Solutions

  1. Regenerate the cookie from a matching (same-version) CrowdSec instance
  2. Ensure all instances sharing master_secret run compatible versions with identical ChallengeCookie proto definitions
  3. Do not attempt to parse attacker-controlled cookies further; treat as invalid and issue a new challenge
  4. If you control the issuer, re-seal with the current proto schema

Example fix

// before
env, err := rt.OpenCookie(raw, now)
if err != nil { http.Error(w, "bad cookie", 400) }
// after
if errors.Is(err, challenge.ErrCookiePayload) {
    log.Debug().Err(err).Msg("invalid cookie payload; issuing new challenge")
    return issueNewChallenge(w, r)
}
Defensive patterns

Strategy: try-catch

Validate before calling

if errors.Is(err, challenge.ErrCookiePayload) { /* invalid payload */ }

Type guard

func isCookiePayloadError(err error) bool { return errors.Is(err, challenge.ErrCookiePayload) }

Try / catch

env, err := rt.OpenCookie(raw, now)
if errors.Is(err, challenge.ErrCookiePayload) {
    log.Debug().Msg("invalid cookie payload; rejecting and re-issuing")
    return issueNewChallenge()
}

Prevention

When it happens

Trigger: openCookie is called with a cookie whose trailing bytes after the reason section are not a serialized pb.ChallengeCookie — e.g. produced by a binary with a different protobuf schema, or a truncated/forged cookie.

Common situations: Mixed CrowdSec versions where the cookie payload schema differs; cookies corrupted in transit or by storage; an attacker-supplied cookie value; a build where embedded protobuf definitions are out of sync.

Understand the failure class

Background: "cannot parse invalid wire-format data", "cannot unmarshal", "failed unmarshalling": protobuf unmarshal errors explained — this error's family across 10 libraries.

Related errors


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