crowdsecurity/crowdsec · error

failed to seal challenge cookie

Error message

failed to seal challenge cookie: %w

What it means

After full validation, ValidateChallengeResponse seals an AppsecCookie under the master cookie key with an embedded not_after (cookieTTL) and the request's User-Agent. If sealCookieV0 fails, the error is wrapped as 'failed to seal challenge cookie' and no cookie can be issued even though the PoW itself succeeded.

Solutions

  1. Check the AppSec configuration for a valid, non-empty cookie secret / key material and restart.
  2. Review the wrapped error: if it reports size, increase maxCookieLen or shorten cookie contents.
  3. If it appeared after a key-rotation change, verify keys.MasterCookieKey() is initialized before validation runs.
  4. Report upstream with the wrapped error if it reproduces with default config — it indicates an internal invariant bug.

Example fix

# before: appsec config missing cookie secret
appsec:
  challenge:
    cookie_ttl: 30s
# after: provide key material
appsec:
  challenge:
    cookie_ttl: 30s
    cookie_secret: <strong-random-secret>
Defensive patterns

Strategy: try-catch

Validate before calling

// preflight in config loading: reject empty cookie key early
if len(keys.MasterCookieKey()) == 0 {
    return errors.New("appsec: master cookie key is empty; cannot seal challenge cookies")
}

Try / catch

cookie, fp, diff, err := rt.ValidateChallengeResponse(req, body)
if err != nil && strings.Contains(err.Error(), "seal challenge cookie") {
    logger.WithError(err).Error("cookie sealing failed; check cookie key config and maxCookieLen")
    http.Error(w, "internal error", http.StatusInternalServerError)
    return
}

Prevention

When it happens

Trigger: Calling ValidateChallengeResponse when sealCookieV0 returns an error — nil/invalid master cookie key, envelope serialization failure, cookie value exceeding maxCookieLen, or invalid inputs (empty key material after misconfiguration).

Common situations: Misconfigured or empty master cookie key in AppSec config; a User-Agent/cookie combination producing a sealed value longer than maxCookieLen (extremely long UA headers); internal key-rotation state corruption.

Related errors


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

Appendix: source

Thrown at pkg/appsec/challenge/challenge.go:725

			"is_bot":  fpData.FastBotDetection,
		}).Debug("validated submission")
	}

	// Seal the difficulty the client actually proved (MAC-authenticated above),
	// so the next request's re-challenge check compares against real work and an
	// escalated per-request difficulty survives the cookie round-trip.
	envelope := &pb.ChallengeCookie{
		Fingerprint:   fpData.ToProto(),
		PowDifficulty: int32(clientDifficulty),
	}

	// Seal under the long-lived master cookie key. The embedded not_after makes
	// the server validity window exactly c.cookieTTL (independent of key
	// rotation); the browser Max-Age below matches so both expire together.
	notAfter := time.Now().Add(c.cookieTTL).Unix()
	cookieValue, err := sealCookieV0(envelope, c.keys.MasterCookieKey(), notAfter, 0, "", []byte(request.UserAgent()), c.maxCookieLen)
	if err != nil {
		return nil, FingerprintData{}, 0, fmt.Errorf("failed to seal challenge cookie: %w", err)
	}

	ck := cookie.NewAppsecCookie(ChallengeCookieName).HttpOnly().Path("/").SameSite(cookie.SameSiteLax).ExpiresIn(c.cookieTTL).Value(cookieValue)
	if request.URL.Scheme == "https" {
		ck = ck.Secure()
	}

	return ck, fpData, clientDifficulty, nil
}

// SealAllowlistCookie mints an allowlist-bypass cookie (no fingerprint, with
// the operator reason) so GrantChallengeCookie can let trusted bots skip the
// challenge UI while still hitting on_challenge rules via fingerprint.Allowlisted.
// not_after honors c.cookieTTL unless ttlOverride (>0) is given; reason is
// bounded by MaxAllowlistReasonLen (crypto.go).
func (c *ChallengeRuntime) SealAllowlistCookie(request *http.Request, reason string, ttlOverride *time.Duration) (*cookie.AppsecCookie, error) {
	if c == nil {
		return nil, errors.New("challenge runtime not initialized")

View on GitHub (pinned to 909b515798)