no-dynamic-algorithm-selection
Disallow dynamic algorithm names in Node.js crypto functions (CWE-327)
CWE: CWE-327 OWASP: A02:2021 — Cryptographic Failures
Node's crypto functions take the algorithm as a string, and accept every algorithm
OpenSSL supports — including the broken ones. When that string is dynamic, whoever
controls it chooses the cryptography: md5 instead of sha256, rc4 instead of
aes-256-gcm, or a name that does not exist at all, which throws at runtime in a code
path that was supposed to be a security boundary.
This is a downgrade attack expressed as a configuration read.
Rule details
Reports when the algorithm argument to createHash, createHmac, createCipheriv,
createDecipheriv, createSign or createVerify is not a static string.
Examples of incorrect code:
const crypto = require('crypto');
// The caller picks the hash. `?alg=md5` downgrades every signature check.
function hash(value, algorithm) {
return crypto.createHash(algorithm).update(value).digest('hex');
}// Environment-driven, which means deploy-time configurable, which means
// a misconfigured environment silently weakens production.
crypto.createHmac(process.env.HMAC_ALG, key).update(body).digest('hex');// A stored value is still a dynamic value.
crypto.createCipheriv(record.cipherName, key, iv);Examples of correct code:
const crypto = require('crypto');
// Static, auditable, greppable.
const hash = (value) => crypto.createHash('sha256').update(value).digest('hex');// A closed allowlist keeps the flexibility without the downgrade.
const ALGORITHMS = { strong: 'sha256', stronger: 'sha512' };
function hash(value, tier) {
const algorithm = ALGORITHMS[tier];
if (!algorithm) throw new Error(`unsupported tier: ${tier}`);
return crypto.createHash(algorithm).update(value).digest('hex');
}Why an allowlist satisfies the rule
The rule asks a narrow question: can the algorithm be steered to a value the author did
not choose? A lookup into a const object literal whose values are all string literals
cannot be — the closed set is visible at the call site and reviewable in the diff.
Legacy interoperability
Verifying signatures from a system that still emits SHA-1 is a real requirement. Name the weak algorithm as a literal in the one function that needs it, and disable the rule there with a comment saying which counterparty requires it. That leaves an artefact a future reader can act on, which a dynamic string does not.
When not to use it
Disable it in a cryptographic test suite that deliberately iterates over algorithms, or in a benchmark. Do not disable it in application code.
Related
Did this rule catch something? Star the repo to get new CWE coverage as we ship it — or follow the AI-code-security benchmarks behind these rules.