Closing a PHP Mail Form Vulnerability

I wrote a PHP script that accepts e-mail from web site visitors using a feedback form. The script works with different sites, routing mail to the right inbox with a hidden field on the form:

The who field doesn't specify an e-mail address, because that would be easy pickings for spammers. They crawl the web looking for e-mail scripts that can be configured to send e-mail to any recipient they specify.

Instead, my script was written to send mail only to accounts on my server:

$recipient = $_REQUEST['who'];

if ($recipient == "") {
$recipient = "";
} else {
$recipient = $recipient . "";

Recently, a spammer found a way to make my script send e-mail to anyone on any server, generating hundreds of spams on my machine over a space of four days.

I'm curious to see if any programmers spot the giant honking vulnerability in the preceding code that I missed for months.


$recipient = "; rogers";

Also, we need to talk about your brace style.


Note to self: crank up the aggregator refresh rate, so you get there while the question's still open, not after the One True Brace Style "discussion" has started.

I figured it wouldn't take long, but eight minutes? Dr. Ecco's Omniheurist Corner this ain't.

Talk about late to the game, I'm here after Visitor. I notice Rogers isn't using rel="nofollow" on those links.

I was going to suggest

$recipient = " ";

since the failure to deliver to "" won't stop greg's mail from going through, although if you read your postmaster mail, you might have noticed the spate of failures. I guess that is actually less likely than noticing the spam to ""

A thrifty, lazy or ambitious spammer could also send more than one mail at a time

$recipient = ",,,,, ";

That's what the spammer did -- 20-25 recipients at a time.


Before you do ANYTHING with input from 3rd party sources, you should "sanitize" it and only allow through characters that you think are legit - here's an example of the basic idea of what I do. There's more validation checking you should do, but this is an excellent first step to ensure the input is semi-legit.


foreach $_ ( sort keys %ENV ) {
$value = $ENV{$_};
$value = sanitize($value);
Never use $ENV after that.

sub sanitize {
my $input = $_[0];
my $OK_CHARS='() "/.,'_@&#-+:=!?a-zA-Z0-9';
$input =~ s/n/__n__/g;
$input =~ s/r/ /g;
$input =~ s/[^$OK_CHARS]/___/go;
return $input;

Things that I do that will help the script above:

1. use str_replace to change ";", ":", "," and so forth to " " (a space);
2. explode the 'to' string, using " " as the delimiter;
3. only send the email to $whoarray[0] -- or perform more checks on $whoarray[0] to verify it's OK. At least this way, your default will not fail (explode-ing a string with no delimiters returns the entire string), but they cannot slip multiple email addresses on a single line. If they have to delimit it to send multiple emails, make sure you break up their delimiters, too.

I wrote an essay for work about the need for escaping; I really need to get around to cleaning it up for public posting this week.

Buffer overflows are the number one security problem, but this sort of problem is number two, coming up on #1 fast, and going to be harder to eliminate the way a language like Java can (nearly) eliminate buffer overflows.

I'm a programmer and I wrote an email form in php (multilanguage) which seems to satisfy my privacy :-)

try it !

Best regards.

Alessandro Marinuzzi

Hi Rogers,

I had a similar problem of a mail form script getting exploited.

In my case it was due to injection of headers. Which your code is not yet safe from.

Check out:

...for the scary details. Basically, like the sanitize comment above, you need to remove line feeds from all variables that will get sent to your system mail function.



Somebody hacked my mailsending script wich was password protected. The script alowwed anybody with the right username and password to send mail anywhere they liked. The hacker sent 50 000 mails in two days.

Hi! The problem of headers injection is solved using this code:

$antispam = array("rn","r","n","%0a","%0A","%0d","%0D",";","Content-Type:","to:","cc:","bcc:");
$name = str_replace($antispam, "", stripslashes($_POST['name']));
$subject = str_replace($antispam, "", stripslashes($_POST['subject']));
$mail = str_replace($antispam, "", stripslashes($_POST['mail']));

$message is the 3 parameter in mail() function and doesn't need to be cleaned since the problem came with the 4 parameter in mail() function.

Bye bye :-)

Add a Comment

All comments are moderated before publication. These HTML tags are permitted: <p>, <b>, <i>, <a>, and <blockquote>. This site is protected by reCAPTCHA (for which the Google Privacy Policy and Terms of Service apply).