Skip to content

auth.flow.weak-bcrypt-rounds

A bcrypt cost factor below 10 was used. A low work factor makes the

OAuthLint idAUTH-FLOW-007
SeverityWARNING
LLM prevalenceMEDIUM
CWECWE-916
OWASPA02:2021
Languagesjavascript, typescript
Technologiesbcrypt, bcryptjs

Why this matters

A bcrypt cost factor below 10 was used. A low work factor makes the hash cheap to compute, which lets an attacker brute-force stolen password hashes far too quickly. OWASP recommends a bcrypt cost of at least 10, and ≥ 12 for new applications, tuned so a single hash takes roughly 250ms on your hardware.

Common LLM-generated mistake: bcrypt.hash(pw, 8) or bcrypt.genSalt(5) because the literal "looks fast enough". Raise the cost factor to 12 or higher.

❌ Vulnerable

ts
// `bcrypt` here is the bcryptjs package — the API surface is identical and
// LLMs frequently alias it to `bcrypt` on import.
import bcrypt from 'bcryptjs';

// ruleid: auth.flow.weak-bcrypt-rounds
export async function hashPassword(pw: string) {
  return bcrypt.hash(pw, 8);
}

// ruleid: auth.flow.weak-bcrypt-rounds
export async function makeSalt() {
  return bcrypt.genSalt(5);
}

// ruleid: auth.flow.weak-bcrypt-rounds
export function hashSyncPassword(pw: string) {
  return bcrypt.hashSync(pw, 9);
}

// ruleid: auth.flow.weak-bcrypt-rounds -- hash with 3-arg callback form
export function hashWithCallback(pw: string) {
  bcrypt.hash(pw, 4, (_err, _hash) => {});
}

// ruleid: auth.flow.weak-bcrypt-rounds -- genSaltSync low cost
export function makeSaltSync() {
  return bcrypt.genSaltSync(6);
}

✅ Safe

ts
import bcrypt from 'bcrypt';

const saltRounds = 12;

// ok: auth.flow.weak-bcrypt-rounds
export async function hashPassword(pw: string) {
  return bcrypt.hash(pw, 12);
}

// ok: auth.flow.weak-bcrypt-rounds -- exactly at the recommended floor
export async function makeSalt() {
  return bcrypt.genSalt(10);
}

// ok: auth.flow.weak-bcrypt-rounds -- cost factor comes from a constant
export function hashWithConstant(pw: string) {
  return bcrypt.hashSync(pw, saltRounds);
}

// ok: auth.flow.weak-bcrypt-rounds -- cost factor from config variable
export function hashWithVar(pw: string, rounds: number) {
  return bcrypt.hash(pw, rounds);
}

Suppressing this rule (when you really must)

ts
// oauthlint-disable-next-line auth.flow.weak-bcrypt-rounds -- <reason>
thisLineWouldOtherwiseTriggerTheRule();

Disable directives are line-scoped by design — wholesale silencing of a rule across the codebase is intentionally not supported, because the next reviewer needs to see exactly which lines opted out.

References

Released under the MIT License · powered by Semgrep