Okta Bcrypt incident lessons for designing better APIs

Bcrypt truncation and API design

  • Many commenters focus on bcrypt’s 72‑byte input limit and silent truncation as a fundamentally bad API design for security‑sensitive code.
  • Several languages/libraries expose both “raw bcrypt” and “non‑truncating” variants; criticism is that the unsafe, interoperable one is the default, while safer versions are longer‑named or hidden.
  • Suggested alternatives:
    • Fail loudly (error/exception) on >72 bytes or on NUL bytes.
    • Make the strict/safe API the default, and move legacy/“raw” bcrypt into an explicitly dangerous/hazmat namespace.
  • Some defend the current behavior for interoperability and legacy compatibility, but others argue that “compat” is not worth the footgun.

Password hash vs KDF and misuse of bcrypt

  • Strong agreement that bcrypt is a password hashing function, not a general key derivation function; it’s designed for short, low‑entropy secrets plus salt.
  • Okta’s usage (hashing userId + username + password for a cache key) is seen as misusing a password hash where a general KDF or plain hash would have been more appropriate.
  • There’s extended discussion clarifying distinctions:
    • Password hashes: slow, salted, produce verifier strings.
    • KDFs: derive keys of specific sizes from higher‑entropy inputs, often with different cost tradeoffs.
  • Some note the naming confusion in the ecosystem (bcrypt historically called a KDF) and the resulting developer misunderstanding.

Why mix username/userId/password into a cache key?

  • Hypotheses:
    • Ensure cache entries differ per user and password.
    • Auto‑invalidate cached auth data when a password changes.
  • Several commenters argue this is overcomplicated and risky: better to store user‑scoped data (e.g., password version or last‑credential‑change timestamp) and/or the bcrypt hash itself, instead of the raw password.
  • Others point out that pre‑hashing before bcrypt introduces additional gotchas (NUL handling, encoding).

Was bcrypt the real bug?

  • Some argue the deeper bug is algorithmic: treating a hash as a unique key without validating the underlying data, ignoring that any fixed‑width hash can collide.
  • Others counter that with a strong 192‑bit bcrypt output, real‑world collision risk is effectively negligible; the practical issue was the truncation behavior, not generic hash collisions.

Library ecosystem and examples

  • PHP’s password_* API is cited as harder to misuse (no expose‑your‑own‑salt hash function, just password_hash and password_verify).
  • Other libraries (e.g., Common Lisp’s Ironclad) are praised for explicitly rejecting overly long inputs.
  • Rust, Zig, and other ecosystems are discussed as partial successes/partial failures in API design around truncation flags and “non_truncating_*” functions.

Broader lessons and reactions

  • Many characterize Okta’s design as a “rookie mistake,” especially for an auth company, and use it to argue that security vendors often employ ordinary generalist developers without deep crypto review.
  • The thread repeatedly reinforces: don’t invent ad‑hoc constructions; use well‑designed KDFs (PBKDF2, scrypt, Argon2, HKDF, libsodium primitives) and APIs that fail safely.