Fix broken cryptographic primitive protecting a secret in Sails.js

A hash, cipher, or random source that cannot carry the weight it has been given: MD5 or SHA-1 over a password, a DES or ECB cipher, or Math.random() producing a token. Each has a drop-in replacement in the standard library, so the fix is small - the cost of not making it is that the protection is decorative.

high likely Sails.js CWE-327 / OWASP A02:2021

The vulnerable pattern in Sails.js

HIGH likely Broken cryptographic primitive protecting a secret A02:2021 api/controllers/account.js:5:35 3 │ // weak-crypto: MD5 over a password. 4 │ function hashPassword(password) { 5 │ const passwordHash = createHash('md5').update(password).digest('hex') │ ~~~~~ fast hash protecting a credential 6 │ return passwordHash 7 │ }

This finding comes from the Sails.js fixture in the owlwarden test suite, in /account. This hash is fast, and speed is the attacker's advantage: a commodity GPU tries billions of candidates a second, so a leaked table of these hashes is a leaked table of the values behind them. Password hashing needs a deliberately slow algorithm with a per-value salt.

The corrected handler

Replace the primitive at the point of use in the model or service.

import { randomBytes, randomUUID, scrypt } from 'node:crypto'

// Tokens and session ids: unpredictable, not merely random-looking.
const sessionId = randomUUID()
const resetToken = randomBytes(32).toString('base64url')

// Passwords: a slow hash with a per-password salt. bcrypt and argon2 are
// equally correct; scrypt needs no dependency.
const salt = randomBytes(16)
const hash = await new Promise<Buffer>((resolve, reject) =>
  scrypt(password, salt, 64, (error, key) => (error ? reject(error) : resolve(key))),
)

If you are not using Sails.js

Use a slow, salted hash for passwords and a cryptographic random source for tokens. Both are in the Node standard library; neither needs a dependency.

Check your own repository

npx owlwarden scan
npx owlwarden explain weak-crypto

Runs on your machine. No account, no telemetry, no network unless you ask. In CI, SARIF uploads to code scanning and the exit code is the gate.

Other Sails.js checks

Rules with a tested Sails.js example.

weak-crypto for every framework / All rules / owlwarden