Assume I have a public API endpoint which I'd like to protect against abuse. I'd like to implement a solution that ensures that the client spends some computational time before allowing the request to succeed.

What would be the problem with having the server choose a random hash and a variable cost of say 200,000, wherein the server would send the hash and the cost to the client, and the client would be required to run the hash through PBKDF2 200,000 times, which should take some number of seconds. The client would then send the result back to the server with their request, and the server verifies that 200,000 iterations of the hash indeed matches the client result.

This would in theory seem to severely limit the number of requests a spammer can execute.

But the idea sounds too simple to be effective. Am I missing something, or would this be a viable solution?

There are two potential problems I can think of:

  1. The spammer can force our own server to waste computational time on calculating these hashes, but we can precompute certain hashes with certain costs and dish out a random entry from our table to any requester.

  2. Client devices may be less powerful than a potential abuser's machine, and thus we might require a cost of say 2,000,000 to be an effective deterrent, but that cost would be too high for an average client device.

  • 1
    This seems a very good way to create a painful self-inflicted DoS. Attackers just have to connect, send anything back, and laugh while your server does 200,000 PBKDF2 iterations... – ThoriumBR Jul 24 '19 at 1:42
  • Right, see potential problem #1. We can precompute hashes. – Snowman Jul 24 '19 at 1:47
  • You will have to store a large table of precomputed hashes, or the attacker will be able to do the same. – ThoriumBR Jul 24 '19 at 2:28
  • @Snowman why do you need a proof of work challenge? It would waste computational resources of your clients. Why not just require a delay between requests? – Andrew Morozko Jul 24 '19 at 12:01

Try to use a Proof of Work algorithm, like the one used on bitcoin: send the client some data, ask for it to send back data:padding where SHA1(data:padding) starts (or ends) with an arbitrary number of zeroes. Client will have to calculate the requested hash a lot of times, and you will have to check only once.

| improve this answer | |
  • This sounds good. Why is this not common in web applications and general APIs? This might also suffer from the problem though where attacker devices are much more powerful than an average user device. – Snowman Jul 24 '19 at 1:51
  • If attacker returns too quickly, ask a harder hash... – ThoriumBR Jul 24 '19 at 2:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.