Secure your contact form!

This post is more than 18 years old.

Posted at 13:40 on 13 December 2005

For the past week and a half a hacker seems to have been trying to use my contact form as a relay for sending spam.

I was first alerted to this at the start of last week when I received a couple of e-mail messages through my contact form that contained a garbled subject line and a message body that appeared to consist mainly of SMTP headers. Checking my website access logs indicated that they were specifying no user agent string and coming from a wide variety of IP addresses, which suggests to me that they were doing some kind of IP address spoofing.

I locked off my blog to POST requests from visitors with no user agent, and added a bit of code to save fuller details of these requests to a text file. One such request gave me this posted data:

UIMessage = thy9762@jamesmckay.net
UISubject = thy9762@jamesmckay.net
UIEmail = thy9762@jamesmckay.net
UIName = floated
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Subject: solicitude of my noble friends got the start of
bcc: charleselegbed@aol.com

53d44f59854c6571b004d87de523ce99
.

What is going on here? The variables UIMessage, UISubject, UIEmail and UIName are (or rather, were) the names of the input fields on my contact form. What this hacker seems to have done is to submit a request to my contact form containing the following information in the "UIName" field:

floated
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Subject: solicitude of my noble friends got the start of
bcc: charleselegbed@aol.com

53d44f59854c6571b004d87de523ce99
.

The net effect of this would be to stick some additional headers into the e-mail -- including this bcc at the bottom to charleselegbed@aol.com, whoever he is. A Google search for this e-mail address finds a lot of apparently similarly compromised guestbooks, blog comments and the like, so presumably he's running some kind of bot.

The messages are coming fairly regularly, about two an hour between 5pm and 8am UTC or so, from a wide variety of IP addresses, which seems to indicate that they are being spoofed. It is possible to spoof your IP address but it does mean that you won't receive a response from the web server. However, the hacker clearly isn't interested in the response here: he's more interested in seeing if his probing e-mail gets delivered, which he will be able to determine from the 32-character string at the top of the message subject. If he does, then the next stage would be to submit another request to your website using the relevant knowledge to send out a whole lot of spam.

I checked my e-mail logs to see if he had attempted this. Sure enough, he had, but his attempts had failed, thanks to a rather convenient size restriction that my ISP places on SMTP headers in e-mail messages.

Armed with this knowledge, I quickly added some code to my contact form to reject any submissions that include a newline character where there shouldn't be one. I am also checking the sender's e-mail address a lot more carefully as well, and I've renamed the contact form, the input fields on it, and the address to which contact e-mail messages are sent. However, I'm still getting regular requests for the original contact form. Since they are using IP address spoofing, they wouldn't be receiving the 404 status code, but it's still a bit odd, given that they won't have received any probing e-mails for several days now.

The vulnerability particularly seems to affect PHP's mail() function, which relies on the "additional headers" parameter for things such as setting the "From:" address, the priority, and so on. This parameter is supplied in the form of a string, which is blindly appended to the end of the SMTP headers in your e-mail message. This is in contrast to how it is done in ASP.NET, for instance, where additional headers are much more sensibly specified as a dictionary of key-value pairs.

To avoid problems with this, you need to validate your user input carefully. First, check that anything that is supposed to be a single-lined value actually is a single-lined value, i.e. it doesn't contain carriage returns ('r') or linefeeds ('n'). Secondly, check to ensure that anything that is supposed to be an e-mail address actually is an e-mail address. The blogging software that I use, WordPress, has this rather useful function:

function is_email($user_email) {
  $chars = "/^([a-z0-9+_]|-|.)+@(([a-z0-9_]|-)+.)+[a-z]{2,6}$/i";
  if(strstr($user_email, '@') && strstr($user_email, '.')) {
    if (preg_match($chars, $user_email)) {
      return true;
    } else {
      return false;
    }
  } else {
    return false;
  }
}

Finally, if you are constructing your e-mail address in the form "name" <email@address>, make sure that you strip out any characters that might confuse it from the name that the user supplies, such as quotation marks, square or angle brackets, and so on.

My initial tests on ASP.NET's System.Web.Mail classes indicate that they are somewhat more robust, but this probably won't stop them having a go regardless. This can be a little bit annoying, so it's best to stick in some more validation anyway. You can never be too careful, after all!