4

While writing server code for resend confirmation page something came to my mind: an attacker could make several requests to /resendemail?user=blah (example URL) and flood such user with registration emails (of course, as long as the user exists and hasn't confirmed their email).

My first idea was to limit request per-session, however, this can be bypassed very easily. Per-IP limit is too much trouble too implement and can be bypassed too (i.e. using several proxies), so I'm confident there must be something simple enough to mitigate this sort of exploit.

Nacib Neme
  • 1,194
  • 2
  • 9
  • 11

4 Answers4

7

Rate-limit on a per-user or per-email-address basis. Since re-sending confirmation emails is rare, setting the limit absurdly low (say, two re-sends per day) should be sufficient to keep from flooding a user.

Mark
  • 34,390
  • 9
  • 85
  • 134
5

Add a time stamp to the email field, if an email has been resent in the last x minutes do not send another email. I would not use a captcha as bots are better at solving those than humans are anyway.

wireghoul
  • 5,745
  • 2
  • 17
  • 26
  • Depending on the captcha, you are correct. Currently, there is one type of captcha (with images and text) that hasn't been cracked using computer power. – Ismael Miguel Mar 02 '15 at 12:23
4

At work, I've implemented a very rudimentar and basic system to a contact form.

Each IP is only allowed to send 5-9 emails a day (random number generated by IP).

You can adapt this and add some options to (for example), allow only 1 or 2 per day in case the user asks it in a somewhat constant manner.

Or block at all and only allow to resend if a specific code is introduced.

You send a recovery code with a specific link.

Then that person visits the link, introduces the code and only then the person can actually recover the password.

Ismael Miguel
  • 141
  • 2
  • 8
  • Why would you randomly assign a limit? – Jon Mar 02 '15 at 06:23
  • @Chipperyman So that no one has a fixed number of tries. An automated script to make requests might blindly request 200 codes. This way, only a very few requests will make it through. This helped to minimize the ammount of spam we were getting on one of our systems (where I've implemented the random limit). I've found it to work quite well. Some may disagree, but I'm happy with the result. – Ismael Miguel Mar 02 '15 at 09:16
  • 3
    @IsmaelMiguel How exactly random limit is better than fixed one? – Cthulhu Mar 02 '15 at 11:28
  • @Cthulhu I'll try to explain in a different way: Imagine that a system gives 10 attemps a day. You can write a code that will send 10 tries. Then it bails out and uses another IP to try more 10 tries. Imagine that now, you don't have a fixed limit. Your limit is randomly betwen 5 and 10. You can send the 10 tries, but up to 5 tries will be complete waste. And then, you have to change to a different IP just to try to hit half the times, and repeat. Sure, you can just go and use 5 tries, but is it really worth to aim for low? Unless it is something worthy, no one (sane) will do this. Right? – Ismael Miguel Mar 02 '15 at 12:22
  • So just make the limit 5 then. It's not worth for attacker to aim that low, right? – domen Mar 02 '15 at 13:15
  • @domen But then, the attacker would try 5 times. You missed the point: the "matter" is the ratio between the number of tries and the number of tries that is guaranteed to be successfull. If you have 10 computers trying 100 tries, there can be up to 50 tries that won't complete. Do you really want to spend your resources for 50 attempts that won't work? – Ismael Miguel Mar 02 '15 at 14:19
  • Apparently, I must be missing something. In what scenario is 5-9 attempts better than just 5 attempts either from user's or attacker's perspective. For user anything >5 might fail, no not useful. And the attacker can just assume 5 tries in both cases. The only slight advantage I see is that it's not immediately obvious to the attacker what the limit of tries is (but if the point was to prevent flooding as in this question, he can just try a big number every time to get the maximum). – domen Mar 02 '15 at 14:33
  • @domen 5-9 is an example. In this scenario, I would choose 3-6 tries. Yes, he can go for 100 tries to try to find the limits. He can go for 3 tries and be happy with that. But he is wasting resources. Every attempt that he can make that he doesn't, is a waste of resorces. Every attempt that isn't successfull will be another waste. Do you like to waste? I don't! Unless computational power is free, it will always be a waste. It prevents flooding, but the random part is optional. The important is a limit. If that limit may be used to make an unnappealing ammout of waste to attackers, even better! – Ismael Miguel Mar 02 '15 at 15:25
  • The typical attacker doesn't care about wasting resources, as long as the waste is less than a few orders of magnitude. They're using a botnet or other automated system, and can just fire-and-forget. – Mark Mar 02 '15 at 20:33
  • @Mark That would put a hole on my method... – Ismael Miguel Mar 02 '15 at 20:42
0

In addition to rate limiting, you can make your email infrastructure report back to your app whether the email was accepted by the destination server or not; and rate-limit only if the emails are getting delivered successfully.

A nice advantage of this method is that your app can tell the user whether the email they sent actually got through and is waiting for them in their inbox or if they should try a different email address.

  • There are a number of ways that an email could be accepted by the destination without ending up in the end-user's mailbox, such as if the email was deleted by a spam or virus filter. – Mark Mar 02 '15 at 10:33
  • @Mark that's why I said *rate limit* and not outright block further sending of mails. –  Mar 02 '15 at 11:16