blog-hero-background-image
Cyber Security

Why bcrypt Will Kill Your API Performance (And What to Use Instead)

backdrop
Table of Contents

Join thousands of professionals and get the latest insight on Compliance & Cybersecurity.


You've set up your API authentication system using bcrypt for your API keys, following security best practices. But as your API traffic grows, you notice alarming performance degradation. Response times are climbing, CPU usage is spiking, and your cloud bill is skyrocketing. What's happening?

The very security measure you implemented to protect your API is now throttling its performance.

While bcrypt is an excellent tool for password hashing, applying it to API key verification is like using a sledgehammer to drive a thumbtack – you're using the wrong tool for the job, and it's going to have serious consequences for your system's performance.

This article will dive deep into why bcrypt is intentionally slow, how that creates a performance bottleneck for APIs, and what high-performance alternatives you should use instead. We'll separate the use cases for password hashing from API key verification and provide specific, actionable recommendations for each.

The Double-Edged Sword: Why bcrypt is Intentionally Slow

Before we dive into performance issues, let's understand an important distinction:

  • Hashing is a one-way function that creates an irretrievable hash
  • Encryption is reversible and requires an encryption key

For password storage, we always use hashing, not encryption. And bcrypt has been a gold standard for password hashing since its introduction in 1999.

The Deliberate Design of bcrypt

Bcrypt was created by Niels Provos and David Mazières specifically to be slow and computationally expensive. This is its core security feature, not a flaw.

The algorithm incorporates a "work factor" (or cost factor) that defines how computationally intensive the hashing process will be. As computing power increases over time, developers can simply increase this work factor to maintain security against increasingly powerful attackers.

Here's the critical insight: according to Auth0's research, "increasing the cost factor in bcrypt from 10 to 20 may significantly increase the time for hashing from 65ms to over 66,000ms per hash." That's a 1,000x increase in processing time!

This exponential slowdown is exactly what you want for password hashing – it makes brute-force attacks computationally infeasible. For context, the OWASP Password Storage Cheat Sheet recommends a minimum work factor of 10 or more for bcrypt.

The Bottleneck: How bcrypt Cripples API Performance

Now let's examine why the intentional slowness of bcrypt becomes problematic for API authentication.

Stateful vs. Stateless Authentication

There's a fundamental difference between user authentication and API key verification:

  • User Login (Stateful): A user logs in infrequently, then receives a session token or JWT. The bcrypt verification happens once, and the cost is amortized across the entire session.
  • API Key Auth (Stateless): Every single API request must be authenticated independently. The verification process runs on every call, making the computational cost of bcrypt a recurring tax on your system.

The Performance Math

Let's do some simple math to demonstrate the impact:

Let's assume a relatively fast bcrypt hash takes 65ms with a work factor of 10 (which is the minimum recommended by OWASP).

  • At 10 requests/second: 650ms of CPU time per second (65% utilization)
  • At 50 requests/second: 3,250ms of CPU time per second (325% utilization - you now need 4 cores)
  • At 100 requests/second: 6,500ms of CPU time per second (650% utilization - you need 7 cores)

As one developer noted on Reddit: "Bcrypt is slow by design, and if you are lucky enough to have a ton of users pounding on your API, just legit requests, your bottleneck will be CPU and memory to verify those passwords."

This performance challenge is particularly acute in languages like Go, where developers have reported significant slowdowns when using bcrypt for high-throughput API authentication.

The consequences are severe:

  • Increased latency for API calls
  • Higher cloud hosting costs
  • Poor user experience
  • Limited API scalability

So what's the solution? It depends on what you're trying to protect.

The Right Tools for the Job: Modern Hashing Alternatives

Let's clarify the two distinct use cases that often get conflated:

For User Passwords (When Slow is Good)

For protecting user passwords, you absolutely want a slow, computationally expensive hashing algorithm. In fact, bcrypt isn't even the most modern option anymore.

The current gold standard is Argon2id, which won the Password Hashing Competition in 2015. It's designed to be memory-hard, making it resistant to both CPU and GPU-based cracking attempts.

According to the OWASP Password Storage Cheat Sheet, Argon2id should be configured with:

  • Memory cost: 19 MiB (minimum)
  • Iterations: 2 (minimum)
  • Parallelism: 1

If Argon2id isn't available in your environment, scrypt is another excellent memory-hard function that improves upon bcrypt.

For API Keys (When Speed is Critical)

API keys represent a fundamentally different security model than passwords:

  1. They are not memorized by humans, so they can (and should) have high entropy
  2. They are typically long and randomly generated (think UUIDs or similar)
  3. The threat model differs - we're not primarily concerned with offline brute-force attacks

Given these characteristics, the recommended approach is to use a fast hash like salted SHA-256:

As one experienced architect advised on Reddit: "You'd better provide enough entropy in the key and it will be unique (think UUIDs) then hash it with sha256."

Why does this work? The security comes from the high entropy of the source key, not the slowness of the hash. A properly generated API key with 256 bits of entropy is computationally impossible to brute-force, regardless of hash speed.

Practical Implementation for API Keys

Here's a secure implementation process for API key hashing:

  1. Generate a high-entropy API key for the user (e.g., sk_live_a1b2c3d4...)
  2. Generate a unique salt for this key and store it with the key record
  3. Hash the API key using SHA-256 with the salt
  4. Store only the salt and the SHA-256 hash in your database
  5. On an incoming request, retrieve the salt, hash the provided key with it, and perform a constant-time comparison against the stored hash

For additional performance improvement, consider this pro tip: Embed a checksum in your API key format that can be validated at the edge (API Gateway/WAF) to cheaply reject malformed keys before they hit your authentication service.

Common Pitfalls to Avoid

Based on OWASP guidelines and real-world experience, here are some common mistakes to avoid:

  1. The Cardinal Sin: Using a fast hash like unsalted MD5 or SHA1 for passwords
  2. The Performance Trap: Using a slow hash like bcrypt for high-throughput, stateless API key verification
  3. Forgetting the Salt: Failing to salt hashes makes them vulnerable to rainbow table attacks
  4. Set-and-Forget: Not periodically reviewing and upgrading your cryptographic tools as computing power increases

Conclusion: Choose Your Algorithm Wisely

Bcrypt is a powerful and essential tool for protecting user passwords, but it's a performance liability when misapplied to API key authentication. Understanding the different threat models is key to making the right choice.

Final recommendations:

  • For user passwords: Use Argon2id (with OWASP-recommended settings)
  • For high-entropy API keys: Use salted SHA-256

Building secure and scalable systems requires using the right cryptographic tools for each specific use case. By following these recommendations, you can maintain both strong security and excellent performance for your APIs.

Remember, in the world of cryptography and authentication, one size definitely does not fit all.

FAQ

Why is using bcrypt for API key authentication a bad idea?

Using bcrypt for API key authentication is a bad idea because its intentionally slow design creates a severe performance bottleneck. Since every API request must be authenticated independently, the computational cost of bcrypt is incurred on every call, leading to high CPU usage, increased latency, and poor scalability as traffic grows.

What is the best hashing algorithm for API keys?

The best hashing algorithm for high-entropy API keys is a fast one like salted SHA-256. The security for API keys comes from their randomness and length (high entropy), not the slowness of the hash function. A fast hash like SHA-256 provides excellent performance for high-throughput systems while remaining secure against pre-computation attacks like rainbow tables, thanks to salting.

If bcrypt is slow, what should I use for user passwords?

You should use a modern, memory-hard hashing algorithm like Argon2id for user passwords. Argon2id is the current industry gold standard, as recommended by OWASP, because it is resistant to both CPU and GPU-based cracking attempts. If Argon2id is not available, scrypt is another strong alternative. The slowness of these algorithms is a critical security feature for protecting user passwords.

How can a fast hash like SHA-256 be secure for API keys?

A fast hash like SHA-256 is secure for API keys because the primary defense is the key's high entropy, not the algorithm's speed. A long, randomly generated API key is computationally infeasible to guess or brute-force. The purpose of hashing the key is to prevent it from being stored in plaintext. By adding a unique salt, you also protect against rainbow table attacks, making it a robust solution for this specific use case.

What's the difference between authenticating a user login and an API key?

The key difference is statefulness. A user login is a stateful, infrequent event; the user authenticates once with a slow hash (like Argon2id) and then uses a temporary session token for subsequent requests. In contrast, API key authentication is stateless; every single request is independent and must be fully authenticated, making the performance of the verification algorithm critical.

How do I properly implement salted SHA-256 for API keys?

To properly implement salted SHA-256 for API keys, follow these steps:

  1. Generate a long, high-entropy API key for the user.
  2. Generate a unique, random salt for each key.
  3. Combine the key and the salt, then hash the result using SHA-256.
  4. Store the salt and the resulting hash in your database, never the plaintext key.
  5. For verification, retrieve the user's salt, hash the incoming API key with that salt, and perform a constant-time comparison against the stored hash.
toaster icon

Thank you for reaching out to us!

We will get back to you soon.