gchq/CyberChef · error · OperationError

Invalid signature!

Error message

Invalid signature!

What it means

Thrown when the recomputed HMAC signature does not equal the signature segment (parts[2]) of the cookie. The operation derives an HMAC key from the secret + salt, signs 'payload.timestamp', converts the digest to URL-safe Base64 without padding, and string-compares it to the user-supplied signature. A mismatch means either the secret/salt/algorithm is wrong or the cookie was tampered with.

Source

Thrown at src/core/operations/FlaskSessionVerify.mjs:111

        const bytes = new Uint8Array(4);
        for (let i = 0; i < 4; i++) {
            bytes[i] = binary.charCodeAt(i);
        }
        const view = new DataView(bytes.buffer);
        const timestamp = view.getInt32(0, false);

        let payloadJson;
        try {
            payloadJson = fromBase64(padded);
        } catch (e) {
            throw new OperationError("Invalid Base64 payload");
        }

        const signB64 = toBase64(sign.finalize());
        const sign64 = signB64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");

        if (sign64 !== parts[2]) {
            throw new OperationError("Invalid signature!");
        }

        try {
            const decoded = JSON.parse(payloadJson);
            if (!args[3]) {
                return {
                    valid: true,
                    payload: decoded,
                };
            } else {
                return {
                    valid: true,
                    payload: decoded,
                    timestamp: timestamp
                };
            }
        } catch (e) {
            throw new OperationError("Unable to decode JSON payload: " + e.message);

View on GitHub (pinned to 4290ea7539)

Solutions

  1. Double-check the SECRET_KEY matches the Flask app's app.secret_key exactly.
  2. Confirm the salt is 'cookie-session' (Flask default) unless the app customized it.
  3. Match the Algorithm arg to the app: older Flask uses sha1, newer may use sha256.
  4. Re-copy the full cookie value including the trailing signature segment to rule out truncation.

Example fix

// before: wrong algorithm
verify.run(cookie, [{string: key, option: 'UTF8'}, {string: 'cookie-session', option: 'UTF8'}, 'sha256', true])
// after: match the app's signer
verify.run(cookie, [{string: key, option: 'UTF8'}, {string: 'cookie-session', option: 'UTF8'}, 'sha1', true])
Defensive patterns

Strategy: try-catch

Try / catch

try {
  const result = flaskVerify.run(cookie, args);
} catch (e) {
  if (e.type === 'OperationError' && e.message === 'Invalid signature!') {
    // signature mismatch: check key/salt/algorithm, do not treat as a crash
  } else throw e;
}

Prevention

When it happens

Trigger: Providing an incorrect secret key; using a wrong salt (Flask default is 'cookie-session'); selecting sha256 when the cookie was signed with sha1 (or vice versa); feeding a cookie from a different application; or a cookie whose payload/timestamp/signature were altered after signing.

Common situations: Wrong SECRET_KEY copied from config; environment mismatch (dev key vs prod key); Flask app using a custom SECURE_KEY or a signer with a non-default salt; copy-paste truncation of the signature segment; algorithm changed across Flask versions.

Related errors


AI-assisted analysis of gchq/CyberChef@4290ea7539 (2026-08-13). Data as JSON: /api/errors/a7f0652ebefd682e. Report an issue: GitHub.