It's with a sense of trepidation that I today reactivated the first of my PHP built webforms since the email injection attack on my site nearly three weeks ago. Ever since then, I've had an ever increasing number attempts on the Hitoplive based forms which remained. None successful of course, but it's meant that I've been a little cautious about unleashing my new script on the world.
But today was the day and now it's life - on one page right now. More at some point soon.
To try and make sure the webform can't be abused in the future, I've spent the last few weeks make a brand new über form, with a wide range of features. Including...
Email addresses are actually validated. Bing! Blindingly obvious thing to do really - email injection is allowed to work by people sneaking new email headers in to an email address box. The characters they need to do this, aren't valid for email addresses. So you can check for strange characters (colons, percentages etc) and solve the problem. In my new script, the address is checked and if there's an illegal character, it's returned to the user to fix. This is the biggy and should be the main thing that will stop another attack on my email forms. But wait, there's more!
All @ symbols will be stripped out and replaced by [at] - with one exception, which is the users address used in the 'From' field. This is of course being validated. No other @ symbols will be allowed in the message. To be honest, this is precautionary, just in case someone finds a way to spam people without attaching stuff to an email address.
The sanitization of certain key phrases. The spammers will send through Cc:'s and Bcc: 's as part of their attempts to spam the world. That's something that's really easy to detect, and more importantly, make safe. Any attempt to send through the phrase 'Cc:' gets filtered into '[ C c ] :'. Nice and obtuse. That should cause them a few problems.
Yeah, I know what you're thinking. What if they send through 'To:' instead? Worry not. I'm filtering that too.
Form fields will be given more obtuse names in the code - and then automatically rot13'd! Now the former is an interesting point. I suspect many people do what I do, and give all form fields a sensible name. A box for email addresses is called 'emailaddress' for example. It makes the code more readable after all. Except it's also readable by scripts and robots trawling sites to hack. So lets not make it too obvious - instead of 'emailaddress', I could use 'theusersemailaddress' or something. Still understandable in the code. Which is why stage 2 comes into play. I'm going to get PHP to rot13 those names so that whilst they're readable when I'm editing the code, it's not (easily) readable to a script.
Now okay, rot13 isn't exactly the worlds most secure encryption. In fact it's patheticly insecure. However it makes things a little harder - not much, but that's the point. There's obviously plenty of easy prey out there, and if you can find plenty of likely candidates simply by searching for 'email' in the code, why bother searching for 'email' once it's been rot13'd? Of course if everyone did this, it would be pointless and the scripts would catch on, and of course it doesn't stop scripts that just randomly populate any form field they find. But still, it's a barrier, even if it is only a couple of centimetres high.
'from' email address is hardcoded in some circumstances. Of course emails usually give you an email address so that when you hit reply, you can send an email instantly back. It's this kind of usability that hackers exploit. Now as I mentioned above, the email address is validated any way, so this step shouldn't be necessary, but just in case...
Except I'm not going to do it all the time, for the sake of my own usability. Most exploits will be done by automated scripts continually sending through fake POST requests to the PHP script. Because when its submitted, the PHP script calls itself but with some different query strings, I can check the HTTP referrer supplied by Apache. So after the POST request has been received, the script checks that the referrer is exactly the same as the current file name.
If the two match, we assume that everything is safe, and we put our validated email address in the email's 'From' field. If they don't, we err on the side of caution, and hardcode it in the script. In both cases, the email address (with all @s replaced by [at]s) is tacked on the end of the email message so it's always available.
Obviously there may be reasons why the referrer details are missing - a firewall may strip them out; a user may disable them for privacy reasons. But importantly, the script still sends the email to me.
It also doesn't stop people from hacking the referrer field as it's sent by the script. So obviously this isn't anything that could stop someone hacking my email forms again. And hey, if someone was doing their hacking automated rather than by script, it's no deterrent at all. But the important thing is, like rot13-ing the email forms, it makes it a little harder.
That said, as a temporary measure - just to make sure the form is okay - I'm hardcoding all From headers to a fixed email address owned by me.
And that's what much of this is all about. It's about extra deterrent.
The email validation in 1 should be the biggest block in the hurdle - I'm not sure how anyone would be able to get through that to be honest.
The conversion of @s into [at]s is the next deterrent. Just in case someone does work out a way of getting email headers into an email without using the email address form box, it won't do them much good for spamming because all their email addresses will be invalid - without an @ in the email, it can't work. Hopefully.
Then to top it all, 3 should stop them trying to put in extra email headers as well.
1-3 are pretty much the big guys - 4 and 5 are then just deterrents. If you can get through the first three, then the 3 and 4 probably can be breached easily enough. But it should be a slog to get there.
That slog is the important bit. Put yourself in the mindset of the hackers who want email scripts so they can spam people. Why spend time trying to break a script with all these deterrents, when there are plenty of other insecure scripts which you can hack with ease?
And that for me is ultimately the problem. PHP at its base is too insecure in this respect. It really needs a simple, secure mail function - one which is far far harder to exploit. By all means give power users some functionality. As long as you give enough warnings, then you might as well. But why are other people offered power they don't need?
The only email headers I needed access to were From, To and Subject. So why isn't there a simple, reasonably secure PHP function that offers that? If there was, we could all rest easy. Not only that, but this entire script would have been unnecessary. And more importantly, millions of people wouldn't receive unsolicited junk mail from hacker, relayed on by unsuspecting website owners.
The new form is currently live on another page. You won't actually be able to see most of the stuff I've mentioned - it's all hidden, apart from the email validation which the user will see a visible response to.