crypto
node:crypto provides hashing, HMAC, random bytes, and ciphers for tokens, webhook verification, and encryption - use built-in APIs before adding npm crypto wrappers.
Search across all documentation pages
node:crypto provides hashing, HMAC, random bytes, and ciphers for tokens, webhook verification, and encryption - use built-in APIs before adding npm crypto wrappers.
import { createHmac, randomBytes, timingSafeEqual, randomUUID } from 'node:crypto';
const token = randomBytes(32).toString('base64url');
const id = randomUUID();
const sig = createHmac('sha256', secret).update(body).digest('hex');When to reach for this:
scrypt/argon2 via crypto or dedicated lib)import { createHmac, randomBytes, scrypt, timingSafeEqual } from 'node:crypto';
import { promisify } from 'node:util';
const scryptAsync = promisify(scrypt);
export function signPayload(body: Buffer, secret: string): string {
return createHmac('sha256', secret).update(body).digest('hex');
}
export function verifySignature(body: Buffer, secret: string, providedHex: string): boolean {
const expected = Buffer.from(signPayload(body, secret), 'hex');
const provided = Buffer.from(providedHex, 'hex');
if (expected.length !== provided.length) return false;
return timingSafeEqual(expected, provided);
}
export async function hashPassword(password: string, salt?: Buffer): Promise<{ salt: Buffer; hash: Buffer }> {
const s = salt ?? randomBytes(16);
const hash = (await scryptAsync(password, s, 64)) as Buffer;
return { salt: s, hash };
}import { webcrypto } from 'node:crypto';
const key = await webcrypto.subtle.generateKey(
{ name: 'AES-GCM', length: 256 },
true,
['encrypt', 'decrypt'],
);What this demonstrates:
timingSafeEqual on digestsscrypt via promisify for password storage with per-user saltsubtle for AES-GCM when interoperating with browser cryptorandomBytes, randomUUID for tokens.| Task | API |
|---|---|
| Session token | randomBytes |
| Webhook verify | HMAC + timingSafeEqual |
| Password storage | scrypt/argon2 |
| AES encrypt | createCipheriv or subtle |
import { createHash } from 'node:crypto';
export function sha256Hex(input: string): string {
return createHash('sha256').update(input, 'utf8').digest('hex');
}=== - leak. Fix: compare HMAC digests with timingSafeEqual.randomBytes only.| Alternative | Use When | Don't Use When |
|---|---|---|
| bcrypt npm | Team standard bcrypt | scrypt native enough |
| KMS envelope encryption | Master keys in cloud | Local dev tokens |
| JWT library | Signed claims with exp | Raw HMAC suffices for webhooks |
| TLS only | Data in transit | Need at-rest field encryption |
UUID v4 for identifiers; randomBytes for custom-length secrets.
URL-safe encoding without +/ - toString('base64url') on Buffer.
Both in crypto - scrypt memory-hard; pick per security review and param tuning.
Node HTTPS server uses tls module - certs from platform or cert-manager.
Stream with createCipheriv for large payloads - subtle for smaller chunks/keys.
Support two secrets during rotation window - verify against both.
Generally OK - avoid sharing keys across threads without clear ownership.
Special Node builds - consult org security - not default generic image.
Pseudonymize with salted hash for logs - not reversible identification.
Buffer Security - wipe and compare patterns.
See Webhook Verification - HMAC pattern.
Use createCipheriv with explicit IV - legacy APIs insecure.
Stack versions: This page was written for Node.js 24.18.0 (Active LTS), npm 10+, TypeScript 5.6+, Express 5, Fastify 5, and NestJS 11.
Reviewed by Chris St. John·Last updated Jul 19, 2026