Skip to main content
interlace
Plugin: node-securityRules

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.

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.