Skip to content

feat(security): pure-CFML bcrypt hash/verify helpers - #3455

Merged
bpamiri merged 2 commits into
developfrom
feat/bcrypt-backlog
Aug 31, 2026
Merged

bpamiri merged 2 commits into
developfrom
feat/bcrypt-backlog

Conversation

@bpamiri

@bpamiri bpamiri commented Aug 31, 2026

Copy link
Copy Markdown
Collaborator

Summary

The final 4.x-backlog item: pure-CFML bcrypt password helpers (#9).

  • vendor/wheels/global/security.cfm — bcryptHash(password, cost=10) / bcryptVerify(password, hash) / bcryptNeedsRehash(hash, cost=10), standard $2b$ 60-char hashes compatible with OpenBSD htpasswd and jBCrypt (verified byte-for-byte against htpasswd output and the official jBCrypt test vectors — including the $2a$ "abc" vector). No Java objects/CFX: runs on Lucee, Adobe, BoxLang, and RustCFML.
  • vendor/wheels/Global.cfc — security.cfm include ladder (same pattern as the other global files).
  • vendor/wheels/tests/specs/security/BcryptSpec.cfc — 12 specs: external-vector verify (true / wrong-password / tampered), jBCrypt reference vector, cost-4 round-trip, 60-char $2b$ format, cost bounds (3/32 throw Wheels.InvalidArgument), verify-never-throws on 7 malformed inputs, unicode round-trip, needsRehash, fresh salt per call.
  • Tutorial doc: the salted-SHA-256 "educational stopgap" aside now recommends bcryptHash()/bcryptVerify() (60-char column).
  • changelog.d/bcrypt-pure-cfml.added.md.

Honest performance note

Pure-CFML bcrypt is inherently slow: ~230 ms at cost 4, ~14 s at cost 10 on Lucee (~170M bit operations; CFML bit-ops are ~60–80 ns each vs native Java's ~100 ms for the same rounds). Correctness and format compatibility are the gate here — cost 10 remains fine for auth events (login/signup), but callers doing bulk hashing should use a lower cost or a native library. Documented in the spec and the changelog fragment.

Verification

  • security area: 302 specs, 0 fail / 0 error (12 new + 290 pre-existing).
  • Known external vectors really verify.

Signed-off-by: Peter Amiri <peter@alurium.com>
RustCFML ships bcryptHash/bcryptVerify as native builtins, so defining
the pure-CFML copies in security.cfm collides at boot ("The name
[bcryptHash] is already used by a built in Function").

Global.cfc now skips the security.cfm include when getFunctionList()
already provides bcryptHash, and always includes the new standalone
security-extra.cfm — bcryptNeedsRehash plus its cost validation have no
engine builtin and ship on every engine.

BcryptSpec skips the pure-CFML-specific pins (the $2b$ prefix and the
Wheels.InvalidArgument cost errors) when the native builtins are in use,
and bcryptNeedsRehash normalizes the parsed cost with Val() so
RustCFML's non-coercing string/number comparison still sees 04 == 4.

Signed-off-by: Peter Amiri <peter@alurium.com>
@bpamiri
bpamiri merged commit 8479de5 into develop Aug 31, 2026
20 checks passed
@bpamiri
bpamiri deleted the feat/bcrypt-backlog branch August 31, 2026 01:12
bpamiri added a commit that referenced this pull request Aug 31, 2026
Records the bcrypt helper's RustCFML native-builtin path and credits
the full 4.x-backlog branch series (#3452-#3455).

Signed-off-by: Peter Amiri <peter@alurium.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant