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
- Double-check the SECRET_KEY matches the Flask app's app.secret_key exactly.
- Confirm the salt is 'cookie-session' (Flask default) unless the app customized it.
- Match the Algorithm arg to the app: older Flask uses sha1, newer may use sha256.
- 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
- Store the exact SECRET_KEY and salt used by the Flask app alongside the cookie.
- Determine the algorithm (sha1 vs sha256) from the app's signer configuration before verifying.
- Treat a signature mismatch as an expected, handled outcome rather than a fatal error.
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
- Secret key required
- Secret key required
- Invalid Base64 payload
- Unable to decode JSON payload: ${e.message}
- Unable to decrypt: authentication failed. The ciphertext, ke
AI-assisted analysis of gchq/CyberChef@4290ea7539 (2026-08-13).
Data as JSON: /api/errors/a7f0652ebefd682e.
Report an issue: GitHub.