Here’s a little story that demonstrates how easy it is to create an insecure system when you try to roll-your-own security protocol.
ScamAlarm users can report fraudulent web sites using a form on our corporate web site. We decided to use a captcha on this form to defend against “bots” submitting zillions of sites into the system. A captcha is one of those slightly annoying graphics with slanty words/numbers that you find on many web forms, most commonly on forms where users can sign up for free accounts. Here’s a sample captcha from the Hotmail signup form:
From a coding point-of-view, here’s how most captcha implementations work:
- Use a random number generator to create a phrase to display to the user
- Save that phrase in the user’s web session. Give the user a cookie that allows you to find their session on your server.
- Show the user a form to display this captcha and receive data. The form has an image tag with a src like this: captcha.php
- Inside the image generator (captcha.php), lookup the phrase in the user’s session and generate the appropriate captcha image
- Validate the typed phrase against the phrase stored in user’s session
Thinking that it would be nice to avoid using cookies for our captchas (we don’t need them for any other purpose on the site) I started to design a cookie-less captcha protocol. If the protocol works, we’ll implement it. Here’s what I came up with:
- Create a secret that’s only available in server-side code to the form processor and the image generator
- Generate a unique random number each time we display the form – the seed
- Create the captcha phrase as the first 6 characters of the sha1 hash of our secret + seed.
- Show the form to the user. Include a hidden form field with the seed value. The form has an image tag with a source like this:
- Inside the image generator (captcha.php), generate the phrase to display (sha1 hash of seed + secret). The seed to use comes as a url parameter.
- Validate the typed phrase against the actual phrase (hash of seed + secret), the seed coming from the hidden form field.
At this point I’m pretty proud of myself. I’ve designed an elegant new captcha protocol that defends against the bot-threat. The bots can’t figure out what phrases we’re going to display because the secret is unavailable to them – its on the server in code. No secret, no threat, right?
Stop right there. Do you see the problem with this protocol? Here’s a hint – the new protocol never checks that the seed hasn’t been used more than one time as the source of a captcha image. The same seed will always generate the same captcha image. Theoretically, only a human can actually read the phrase embedded in the captcha image, but that would only have to happen one time. We’re now vulnerable to a replay attack. The bot programmer could simply look at one of forms, write down the phrase and its seed, and then program their bot to submit zillions of urls to use accompanied by that one seed + phrase combo. The new protocol would have given us no protection whatsoever. Doh!
The moral of the story – its easy to create insecure protocols using cool cryptography functions. Stick with established protocols. We decided to use cookie-based sessions. We could have created a database of seed values and only allowed them to be used once, but cookie-based sessions was simpler to implement and just as effective.