Full Report
Okta had an interesting security incident. If the username was above 52 characters, then ANY password would be sufficient for logging in. If the username was 50 characters, then it would be only two. In Okta, the password hash included the concatenation of the userId, username and password. Why did this happen? The hashing function BCrypt! In BCrypt, there is a limit to the size of the input to 72 characters. Since the user ID and username were included, it was possible to go above this. In some libraries, this would lead to a silent truncation of the data. With how much data Okta was using besides the password, this led to the entire password being truncated. The author was curious about why the library even allowed this in the first place. A simple check on the input length would be sufficient in the library for preventing this from happening. They evaluated several implementations of libraries in different languages, all of which handled this case differently. Some errored out and some silently truncated the data. Why the truncation? They were conforming to the BSD implementation from years ago! My personal favorite part of the article is the end. The author goes into Secure API Design. Recently, this has become a bigger concern of mine in my job so it was interesting to see these come out. The first point is Don’t let the people use your API incorrectly. It should explicitly reject invalid input in order to prevent errors like this. If the functionality is required, then gate it behind a feature flag or unsafe variant of the function. The next point was Be predictable. Good API design should be intuitive and obvious. Of course, this is subjective but we can use some common sense here. No ego is the next one. Expecting users to read every bit of documentation or fully understand the implementations is totally unreasonable. Making systems easy to use for novice users should be the goal, with more advanced functionality being added to the more advanced ones. Good input validation goes a long way here. The final point of note is Be Brave. To the authors point, be the new solution that is better. It's easy to fallback onto old implementations since it's always been there. Do something to make the world a better place. Overall, an interesting read that further evaluates the Okta issue. I enjoyed the parts about secure API design the most that used Okta as a case study.
Analysis Summary
# Best Practices: Secure API Design and Hashing Implementations
## Overview
These practices address the vulnerabilities arising from "silent failures" and "silent truncations" in cryptographic libraries and API integrations. Specifically, it uses the Okta/Bcrypt incident—where inputs exceeding 72 characters were truncated, leading to authentication bypasses—as a case study for building defensive, predictable, and secure interfaces.
## Key Recommendations
### Immediate Actions
1. **Length Validation:** Implement explicit client-side and server-side checks to ensure that any input destined for a Bcrypt hashing function does not exceed 72 characters.
2. **Audit Hashing Logic:** Identify instances where concatenated strings (e.g., `userId + username + password`) are passed to hashing functions. Ensure the combined length is strictly capped.
3. **Library Inventory:** Check if your current Bcrypt library (especially in Java/Spring or older BSD-influenced environments) silently truncates or throws an error on long inputs.
### Short-term Improvements (1-3 months)
1. **Fail-Fast Implementation:** Replace libraries that silently truncate with those that explicitly return errors for invalid input lengths.
2. **Input Sanitation/Separation:** Move away from concatenating sensitive credentials into a single string. If caching keys are required, hash individual components separately or use a delimiter-aware structure that doesn't sacrifice the entropy of the password.
3. **API Schema Strictness:** Update API definitions to reject requests that do not conform to expected length constraints before they reach the business logic layer.
### Long-term Strategy (3+ months)
1. **Secure API Design Framework:** Adopt a "No Ego" design philosophy: assume developers will not read the full documentation and design the API to be "impossible to use incorrectly."
2. **Modernize Crypto Primitives:** Evaluate migrating to hashing algorithms that do not have low maximum input limits (e.g., Argon2), while ensuring backward compatibility strategies are in place.
## Implementation Guidance
### For Small Organizations
- Focus on **Input Validation**. Since your team might be small, stick to well-maintained, "brave" libraries (like Go’s `x/crypto/bcrypt`) that handle edge cases natively.
### For Medium Organizations
- Implement **Unit Testing for Edge Cases**. Specifically, write tests that pass 72, 73, and 500-character strings to your authentication handlers to verify system behavior (error vs. truncation).
### For Large Enterprises
- **Centralized Security Governance:** Standardize a wrapper around cryptographic libraries. Ensure that no developer uses the raw Bcrypt library directly, but rather a company-approved "Secure-Auth-Lib" that enforces length checks and provides "unsafe" variants only via explicit feature flags.
## Configuration Examples
### Defensive Wrapper (Conceptual Go/Java Logic)
Instead of: `bcrypt.hash(user + password)`
Use a validator:
go
func SafeBcryptHash(input string) (string, error) {
// Explicitly reject if over the hardware/algorithm limit
if len(input) > 72 {
return "", fmt.Errorf("input exceeds Bcrypt limit of 72 characters")
}
return bcrypt.GenerateFromPassword([]byte(input), cost)
}
## Compliance Alignment
- **NIST SP 800-63B:** Digital Identity Guidelines (Authentication and Lifecycle Management).
- **OWASP ASVS:** V3.2 (Verify that the password hashing algorithm used has no known vulnerabilities).
- **ISO/IEC 27001:** Control A.8.27 (Secure System Development Life Cycle).
## Common Pitfalls to Avoid
- **Implicit Truncation:** Assuming a library will "handle it" just because it is a standard implementation.
- **Concatenation Overload:** Including too much metadata (UserId, Email, Full Name) in the hash input, which eats into the character budget for the actual password.
- **Documentation Reliance:** Expecting that writing "Max 72 characters" in a ReadMe is enough to prevent a security incident.
## Resources
- **Go Crypto Library:** `golang[.]org/x/crypto/bcrypt` (Known for safe error handling).
- **Bcrypt Documentation:** `en[.]wikipedia[.]org/wiki/Bcrypt#Maximum_password_length` (Technical limits reference).
- **N0rdy Post Samples:** `github[.]com/n0rdy/n0rdy-blog-code-samples` (Vulnerability reproduction code).