v0.14 is out: a mobile auth pack for Swift/iOS and Android, catching insecure token storage, cleartext traffic, and OAuth in embedded WebViews. Read more →
MEDIUM AI PREVALENCE: MEDIUM auth.go.crypto.bcrypt-low-cost

bcrypt.GenerateFromPassword is called with a cost factor below 10.

Why AI tools produce this: AI coding tools produce this regularly, typically when prompted for a shortcut or a quick fix.

Why this matters

A low work factor makes each 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.GenerateFromPassword(pw, 8) because the literal "looks fast enough". Use bcrypt.DefaultCost (10) or raise the cost factor to 12 or higher.

VULNERABLE
vulnerable.go
package main

import (
	"fmt"

	"golang.org/x/crypto/bcrypt"
)

// Cost factor far too low.
func hashWeak(pw []byte) []byte {
	// ruleid: auth.go.crypto.bcrypt-low-cost
	h, _ := bcrypt.GenerateFromPassword(pw, 4)
	return h
}

// Below the recommended minimum of 10.
func hashLow(pw []byte) []byte {
	// ruleid: auth.go.crypto.bcrypt-low-cost
	h, _ := bcrypt.GenerateFromPassword(pw, 8)
	return h
}

// Still one short of DefaultCost.
func hashJustUnder(pw []byte) []byte {
	// ruleid: auth.go.crypto.bcrypt-low-cost
	h, _ := bcrypt.GenerateFromPassword(pw, 9)
	return h
}

func main() {
	pw := []byte("hunter2")
	fmt.Println(hashWeak(pw), hashLow(pw), hashJustUnder(pw))
}
SAFE
safe.go
package main

import (
	"fmt"

	"golang.org/x/crypto/bcrypt"
)

// Uses the package default cost (10). Not a numeric literal < 10.
func hashDefault(pw []byte) []byte {
	// ok: auth.go.crypto.bcrypt-low-cost
	h, _ := bcrypt.GenerateFromPassword(pw, bcrypt.DefaultCost)
	return h
}

// Explicit strong cost factor.
func hashStrong(pw []byte) []byte {
	// ok: auth.go.crypto.bcrypt-low-cost
	h, _ := bcrypt.GenerateFromPassword(pw, 12)
	return h
}

// Cost supplied via a variable, not a literal. Not flagged.
func hashVar(pw []byte, cost int) []byte {
	// ok: auth.go.crypto.bcrypt-low-cost
	h, _ := bcrypt.GenerateFromPassword(pw, cost)
	return h
}

func main() {
	pw := []byte("hunter2")
	fmt.Println(hashDefault(pw), hashStrong(pw), hashVar(pw, 14))
}

Suppressing this rule

If a finding is a genuine false positive, scope the suppression to the exact line and leave a reason, never disable the rule project-wide. Disable directives are line-scoped by design.

// oauthlint-disable-next-line auth.go.crypto.bcrypt-low-cost -- <reason>

References

https://pkg.go.dev/golang.org/x/crypto/bcrypt#GenerateFromPassword ↗https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html ↗https://cwe.mitre.org/data/definitions/916.html ↗