n8n Crypto node: hashing, HMAC and webhook signature verification
Published 1 August 2026 · 7 min read
An n8n webhook exposed to the internet accepts any POST request by default: nothing distinguishes a legitimate event sent by Stripe from a forged request crafted by someone who found the URL. That is exactly why Stripe, GitHub and Shopify sign every webhook with an HMAC: the receiver recomputes the signature with the shared secret and rejects anything that does not match. In n8n, the Crypto node covers this without writing code — and delivers plenty of other value along the way: idempotency keys, change detection, pseudonymization, single-use tokens. This guide walks through its actual operations, shows signature verification end to end, and explains when to switch to a Code node.
The Crypto node's operations
The Crypto node (a core node, no credential needed for most operations) offers six actions:
- Hash: computes the digest of a text value or a binary file. Available algorithms: MD5, SHA256, SHA3-256, SHA3-384, SHA3-512, SHA384, SHA512, with output encoded as HEX or BASE64. For a file, enable the binary option and point to the property name (for example
data). - Hmac: same algorithm list and encodings as Hash, but the digest is keyed with a secret — this is the operation you use to verify webhook signatures and to produce digests that cannot be precomputed.
- Sign: signs a value with a private key (RSA, ECDSA…) and the signature algorithm picked from the node's list, output in HEX or BASE64. Useful when you are the one who must prove a message's origin to a partner API.
- Generate: produces a random string in ASCII, BASE64, HEX or UUID format, with a configurable length (32 by default, except for UUID).
- Encrypt / Decrypt: symmetric encryption with a passphrase, or asymmetric RSA — the latter limited to small payloads (around 190 bytes with a 2048-bit key), so reserve it for short secrets, not documents.
The result lands in an item property (configurable name), immediately usable by downstream nodes through an expression like {{ $json.data }}.
Verifying an incoming webhook's HMAC signature
The principle is the same across providers: the raw request body is hashed with HMAC-SHA256 using a secret only you know, and the result travels in an HTTP header. Three common examples:
- GitHub:
X-Hub-Signature-256header, hex-encoded HMAC-SHA256 prefixed withsha256=. - Shopify:
X-Shopify-Hmac-Sha256header, HMAC-SHA256 encoded in base64. - Stripe:
Stripe-Signatureheader in the formt=timestamp,v1=signature, with the HMAC computed over the concatenationtimestamp.body— a case that needs a little preparation before hashing.
Verification in n8n, for a straightforward case like GitHub or Shopify:
- Webhook node with the Raw Body option enabled: the signature covers the exact bytes of the body, not a JSON re-serialized by n8n. This is the detail that breaks most first attempts — the node's fundamentals are covered in our complete Webhook node guide.
- Crypto node, Hmac operation, SHA256 type, fed with the raw body (binary mode if Raw Body arrives as a binary property), your webhook secret, and the encoding the provider expects: HEX for GitHub, BASE64 for Shopify.
- IF node comparing the computed signature to the received header, for example
{{ $json.data === $('Webhook').item.json.headers['x-shopify-hmac-sha256'] }}(n8n lowercases header names). - False branch: a Respond to Webhook node returns a 401 and the workflow stops there; true branch: processing continues.
This check complements the other protections (node authentication, IP allowlists) described in our article on securing n8n webhooks. The HMAC mechanism itself is anything but improvised: the construction was formalized and proven secure by Mihir Bellare, Ran Canetti and Hugo Krawczyk in "Keying Hash Functions for Message Authentication", published at CRYPTO 1996 — the paper that introduced HMAC, later standardized as RFC 2104 and adopted as-is by the APIs that sign their webhooks today.
For Stripe, the timestamp.body concatenation and the parsing of the Stripe-Signature header are more comfortable in a Code node placed just before the Crypto node — or handled entirely in Code, as shown below. Our article on Stripe webhooks and payment recovery shows the full business-side pipeline.
When the node is not enough: Code node and constant-time comparison
Comparing two signatures with === in an IF node works, but the comparison stops at the first differing character: response time varies with the number of correct characters, which theoretically leaks information. David Brumley and Dan Boneh showed in "Remote Timing Attacks are Practical" (12th USENIX Security Symposium, 2003) that timing attacks, long assumed to be limited to smartcards, can be carried out over a network against real servers. On an n8n workflow whose latency is dominated by the network and the scheduler, exploitation is very hard in practice — but the constant-time version only costs a few lines:
const crypto = require('crypto');
const secret = $env.WEBHOOK_SECRET;
const rawBody = $json.rawBody; // raw body, preserved as-is
const received = $json.headers['x-hub-signature-256'] ?? '';
const expected = 'sha256=' +
crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
const a = Buffer.from(expected);
const b = Buffer.from(received);
const valid = a.length === b.length && crypto.timingSafeEqual(a, b);
return [{ json: { valid } }];
Two prerequisites: the built-in crypto module must be allowed in the Code node through the NODE_FUNCTION_ALLOW_BUILTIN environment variable (the setup is in our guide on npm modules in the Code node), and the secret must come from an environment variable rather than being pasted inline — see our best practices for securing credentials and API keys in n8n. The Code node also takes over for any encoding the Crypto node does not expose (base64url, raw binary output); the general syntax is covered in our guide to expressions and JavaScript in the Code node.
Four more patterns worth automating
Idempotency keys from a payload hash
A provider redelivering the same webhook twice, a form submitted in duplicate: hashing the meaningful payload fields with SHA256 yields a stable key you check against a table (Data Table, Redis, Postgres) before processing. If the key already exists, the item stops there. The full pattern is detailed in our article on webhook idempotency and deduplication.
Detecting changes before an expensive step
Before recomputing embeddings or re-running an LLM call on a document, compare the SHA256 of the current content with the one stored on the previous run: if the digest has not moved, skip the paid step. On a document base re-synced nightly, this simple test avoids re-vectorizing the 95% of files that never changed.
Pseudonymizing personal data in logs
Logging workflow activity without storing plain-text emails: the Hmac operation (not Hash) with a dedicated secret produces a stable identifier — the same email always yields the same pseudonym, preserving correlations — yet irreversible without the secret, whereas a plain SHA256 of an email falls to a dictionary attack. Note: this remains pseudonymization under GDPR, not anonymization. It fits into a broader compliance effort covered by our articles on handling GDPR requests with n8n and building a GDPR audit trail with n8n and Supabase; for ready-made workflows, the Compliance & Audit Pack (€149) bundles the whole setup.
Generating single-use tokens
The Generate operation in HEX format (32 characters or more) produces an unpredictable token for a confirmation, unsubscribe or time-limited download link; the UUID format suits correlation identifiers. Store the token with an expiry date, compare it on receipt, then invalidate it after use.
Best practices and limits
- MD5 is no longer a security algorithm: collisions can be built on purpose. It remains acceptable as an internal deduplication fingerprint, never for a signature or an integrity check against an adversary.
- The raw body is non-negotiable for signatures: any JSON re-parsing before the HMAC computation changes the bytes and breaks the comparison.
- One secret per integration: do not reuse your Stripe secret to sign anything else, and store each secret as an environment variable or encrypted credential.
- The Crypto node is not key management: for the Sign operation, the private key pasted into the node must be treated like any sensitive credential, with planned rotation.
Key takeaways
The Crypto node covers the everyday cryptographic needs of an n8n workflow: Hash and Hmac (MD5 through SHA3-512, HEX or BASE64 output), Sign with a private key, Generate for randomness, Encrypt/Decrypt for small secrets. Webhook signature verification takes three nodes — Webhook with Raw Body, Crypto in Hmac mode, IF for the comparison — and moves to a Code node with timingSafeEqual when you want a constant-time comparison or a format the node does not expose. Add idempotency hashes, change detection and HMAC pseudonymization of logs, and this small credential-free node becomes one of the highest-yield tools in the n8n palette.
FAQ
Frequently asked questions
Can the n8n Crypto node verify a webhook signature by itself?
It computes the expected signature (Hmac operation with your secret), but the comparison against the received header happens in an IF node placed right after it. For a stricter, constant-time comparison, switch to a Code node and use crypto.timingSafeEqual from Node.js's built-in crypto module.
Which hashing algorithms does the n8n Crypto node support?
The Hash and Hmac operations offer MD5, SHA256, SHA3-256, SHA3-384, SHA3-512, SHA384 and SHA512, with output encoded as HEX or BASE64. The node accepts either a text value or a binary file referenced by its property name.
Why enable the Webhook node's Raw Body option before computing an HMAC?
Because the provider signs the exact bytes of the request body. If n8n parses the JSON and re-serializes it, key order or whitespace can change and the computed HMAC will never match the received signature, even with the correct secret.
Is hashing an email address enough to anonymize it under GDPR?
No. A plain hash of an email is pseudonymization, not anonymization: the space of possible addresses is small enough for a dictionary attack to recover the original value. Use the Hmac operation with a separately stored secret instead — without the secret, the dictionary cannot be precomputed.
Bundle FlowKit Complet
€269