Forum Moderators: coopster

Message Too Old, No Replies

PHP email form script being abused or spammed

documentation of problems encountered

         

aggievizzer

2:44 pm on Sep 10, 2005 (gmt 0)

10+ Year Member



By posting this, I wanted to document or "make known" some of the problems I have been having with my online forms being spammed or abused. This post is long, but I tried to document as much as possible.

I would like any suggestions on how to combat this problem if possible. I realize that if you have found a solution to this problem, you might not want to reveal your solution…but any help would be greatly appreciated.

If you have similar problems, and would like to add to this post, please do so. This way we might be able to aid others in combatting this problem with documentation.

====================

Description:

1. Online contact / inquiry forms are being abused or submitted multiple times within a span of about 2-5 seconds. The spammer or bot is filling in all fields, including my hidden fields ( such as IP address grabber and date/time script) with randomly generated email addresses. For example, the emails sent via my forms usually look like this:

IP Address: acvhdhf@domainname.com
Date and Time: acvhdhf@domainname.com
Name: acvhdhf@domainname.com
Phone: acvhdhf@domainname.com
etc….

The spammer or bot grabs the domain name from the website where the online contact / inquiry form resides…and…places that domain on the end of the random email address as shown above.

I am using a javascript user-side check to verify that the email addresses match. This is to guard against valid users from mis-typing their email address. This leads me to believe that this spammer is using a browser script, such as a greasemonkey script, to fill in these forms…So they can get past this email validation. Once past the user-side validation, the script is executed many times.

The execution times vary in the range of 2-5 seconds…so this leads me to guess that it is a human executing the script…not a bot programmed to execute at regular intervals.

The mail scripts can be executed anywhere from 5-95 times during spamming sessions…or until their finger gets tired or they feel satisfied.

-----------------

2. I have checked my server logs, and the spammer or bot uses different IP addresses for different spamming sessions…sometimes they are from US, China, Russia, etc. I have checked the IP's against misuse databases and some are noted as spamming IPs and some aren't. So, I will assume that they are random.

-----------------

3. The typical traversal through a website is homepage -> page1 -> page2 -> page3 -> page4 -> form page -> form script

Then the form script is executed multiple times from the same randomly generated IP address. The submissions stop, and the spammer or bot moves on to another website stored on my webserver.

-----------------

4. I have been dealing with this problem for about a year. I have been adding in validations to the script regularly based on suggestions found online. Unfortunately, the spammer or bot seems to be "upstaging" me for each adjustment I make.

A "game" of cat and mouse…with my time being wasted…I am just trying to pay back my student loans for Pete's sake by obtaining new customers.

This "game" started out with the forms being submitted with blank fields, for which I countered with a validator to guard against this…user-side and server side.

Then I added in the cookie and session check. This was circumvented as well.

The validations, I have added, go on and on.

-----------------

I just wanted to make my problems known so that it might be of some help.

Thanks for your time or any suggestions that you might be able to provide.

zoobie

1:51 am on Sep 14, 2005 (gmt 0)

10+ Year Member



The exact same bot has hit my site, too, right down to the asdf@mysite.com and jrubin3546@aol.com

Day 1, I noticed 30 empty emails.
Day 2, I determined someone or something was posting to my mailer.php script.
Day 3, I added a hidden form field which the mailer script checks for. I still send the spammer to the thank you page but the email isn't sent.

End of problem.

Btw...I thought it was a human but now see it isn't.

Some good tips here...I'll have to read those links.

bnhall

2:45 am on Sep 14, 2005 (gmt 0)

10+ Year Member



If they're doing this via bcc isn't it possible to just look for the characters "bcc" and strip them out before sending an email in the form?

zoobie

3:52 am on Sep 14, 2005 (gmt 0)

10+ Year Member



Just read the links in this post. They offer several snips to remedy the problem. I'm reading them now and the only sure way is to remove the \r\n which can be done several ways.

Since I'm here, I copied a few solutions including, as you mentioned, removing the bcc altogether:

This is what you need to do in PHP form:

Make sure you end your headers with \r\n\r\n.

change

$headers .= "From: " . $from . "\r\n";

to

$headers .= "From: " . $from . "\r\n\r\n";

It is always best to filter mail form inputs

// Strip \r and \n from the email address

$_POST['email'] = preg_replace("\r", "", $_POST['email']);
$_POST['email'] = preg_replace("\n", "", $_POST['email']);

// Remove injected headers

$find = array("/bcc\:/i","/Content\-Type\:/i","/cc\:/i","/to\:/i");

$_POST['email'] = preg_replace($find, "", $_POST['email']);
$comments = preg_replace($find, "", comments);

Another way, assuming they don't change their method, would be:


if(
eregi("\r",$_POST["email"])
¦¦ eregi("\n",$_POST["email"])
¦¦ eregi("@mydomain.net",$_POST["email"])
¦¦ eregi("@mydomain.net",$_POST["message"])
¦¦ eregi("boundary=",$_POST["message"])
)
{
die($sorry_string);
}

Isn't the internet fun? Ugh...

jatar_k

4:06 am on Sep 14, 2005 (gmt 0)

WebmasterWorld Administrator 10+ Year Member



my suggestion

don't change the data, if you find a bad character, send them back to the form, no point in trying to sanitize user data, we only want good data, the bad data can be sent back to fix it

sudders

10:21 am on Sep 14, 2005 (gmt 0)

10+ Year Member



hi. i've been having this problem for a few weeks. i've read all the comments and they've given me a lot of ideas. however, there hasn't been any mention of restricting access via a .htaccess file. i'm quite new to all of this (i just manage my co.'s website), so i'm not sure if it's a good idea, if it's feasible or if it's just crap. would appreciate any thoughts anyone may have on this. thanks.

jatar_k

3:07 pm on Sep 14, 2005 (gmt 0)

WebmasterWorld Administrator 10+ Year Member



Welcome to WebmasterWorld sudders,

you could do that but you would need to find out the ip of the abuser first and then put it into your htaccess manually

preferably we want something that is automated first and then use manual intervention later if necessary

sudders

3:29 pm on Sep 14, 2005 (gmt 0)

10+ Year Member



i had thought of that. and seeing as how it seems that the IPs are spoofed, then there would be no way to block the IP. however, how about only allowing the script to be from the domain (or IP) on which it sits. for example, if the script was on a contact page on mysite.com, then in the .htaccess file only allow it to run from mysite.com. i'm not quite sure if the php form would see that it is being called from an html page residing on mysite.com. if it does, then technically this could work.

then again, this could all be a 'pie in the sky' solution! for some reason, i doubt it works the way i just described.

Hatty

12:25 pm on Sep 19, 2005 (gmt 0)

10+ Year Member



Hello
I've read with interest Zoobie post of 3:52 am on Sept 14, 2005, and this is probably a really stupid question (I know very little php)

but why do I get - Empty regular expression in /home/mydomain.co.uk/public-html/processblank-1.php on line 15 -

Empty regular expression in /home/mydomain.co.uk/public-html/processblank-1.php on line 16 -

line 15 and 16 are
$_POST['email'] = preg_replace("\r", "", $_POST['email']);
$_POST['email'] = preg_replace("\n", "", $_POST['email']);

<?

$name=trim($name);
$address=trim($address);
$telephone=trim($telephone);
$email=trim($email);


if (!ereg("^[a-zA-Z0-9\-\.]+@[a-zA-Z0-9\-]+\.[a-zA-Z0-9\-\.]+$", $email))
{
echo "That is not a valid email address. Please return to the"
." previous page and try again.";
exit;
}
$_POST['email'] = preg_replace("\r", "", $_POST['email']);
$_POST['email'] = preg_replace("\n", "", $_POST['email']);

$toaddress = "kareng@mydomain.co.uk";
$subject = "Brochure request";
$mailcontent = "Name: ".$name."\n"
."Address: ".$address."\n"
."Telephone number: ".$telephone."\n"
."Email: ".$email."\n";


$fromaddress = "webserver@mydomain.co.uk";

mail($toaddress, $subject, $mailcontent, $fromaddress);
?>

zoobie

12:50 pm on Sep 19, 2005 (gmt 0)

10+ Year Member



I'm just guessing but if you used the code I found, it looks like you didn't add the header's double \r\n\r\n that the script required.

[edited by: coopster at 3:50 pm (utc) on Sep. 19, 2005]
[edit reason] removed url per TOS [webmasterworld.com] [/edit]

Dark_Matter

2:02 pm on Sep 19, 2005 (gmt 0)



What about using robots.txt to keep the page from getting indexed in search engines?

aggievizzer

2:27 pm on Sep 19, 2005 (gmt 0)

10+ Year Member



I have tried using the robots.txt file and the "spam relay bot" ignores it.

The bot crawls through the website via links. When it finds the form, it indexes the root url of your website, the location of the contact/comment form page , and the location of the script.

It then randomly executes the script via an absolute url.

If you move or change the name of any of the page or script names, the bot starts at the root url and begins the process again.

RonPK

5:15 pm on Sep 19, 2005 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



> $_POST['email'] = preg_replace("\r", "", $_POST['email']);

No delimiters in the pattern. It should be

$_POST['email'] = preg_replace("/\r/", "", $_POST['email']);

But as there is no need for the overhead of using a regular expression, a simple str_replace will do fine.

$_POST['email'] = str_replace("\r", "", $_POST['email']);

mbyrne

5:30 pm on Sep 19, 2005 (gmt 0)



I am a novice programmer, a website I created is being spammed 15-30x per day nad She recieves these results
>
> Values submitted from web site form:
> Breastfeeding : bpnbbki@example.com
> MORE-INFO : bpnbbki@example.com
> Infant : bpnbbki@example.com
> 04)email : bpnbbki@example.com
> 03)TELEPHONE : bpnbbki@example.com
> 01)FROM : bpnbbki@example.com
> Content-Type: multipart/mixed; boundary=\"===============0609687900==\"
> MIME-Version: 1.0
> Subject: 7738eec
> To: bpnbbki@example.com
> bcc: mhkoch321@example.com
> From: bpnbbki@example.com
>
> This is a multi-part message in MIME format.
>
> --===============0609687900==
> Content-Type: text/plain; charset=\"us-ascii\"
> MIME-Version: 1.0
> Content-Transfer-Encoding: 7bit
>
> jadnqsjc
> --===============0609687900==--
>
> Submit : bpnbbki@example.com
> Prepared : bpnbbki@example.com
> Intro : bpnbbki@example.com
> 02)mailing : bpnbbki@example.com
> Baby : bpnbbki@example.com
> The : bpnbbki@example.com

I edited the thanks.php page does this look right

<SCRIPT LANGUAGE="php">
$email = $HTTP_POST_VARS[email];
$mailto = "client@example.com";
$mailsubj = "Form submission";
$mailhead = "From: $email\r\n\r\n";
reset ($HTTP_POST_VARS);
$mailbody = "Values submitted from web site form:\r\n\r\n";
while (list ($key, $val) = each ($HTTP_POST_VARS)) {
$mailbody .= "$key : $val\n"; }
mail($mailto, $mailsubj, $mailbody, $mailhead);
</SCRIPT>
Thank you for filling out this form we will be contacting you very soon.

[edited by: coopster at 7:55 pm (utc) on Sep. 19, 2005]
[edit reason] generalized email address [/edit]

zoobie

5:35 pm on Sep 19, 2005 (gmt 0)

10+ Year Member



I think Rons right as there's also some unreliability with the preg_replace and preg_match being used. I posted a link to more codes but it was removed for TOS reasons. Funny thing is, I think I found the link here. You can always search the internet by typing in jrubin3546@aol.com or the bcc address used as it's a major problem.

matthijs

5:53 pm on Sep 19, 2005 (gmt 0)

10+ Year Member



I think Rons right as there's also some unreliability with the preg_replace and preg_match being used.

Zoobie, which unreliability do you mean? I'm interested, because I've also followed several threads on different forums and articles about this problem, and still not 100% sure which solution is the best, easiest and gives the least unwanted side-effects. So far I have seen every variant with preg_replace, iregi, str_replace, strpos, strstr, preg_match, etc
(I gues a lot of people are going to increase their level of knowledge on regexps because of this... ;)

aggievizzer

5:57 pm on Sep 19, 2005 (gmt 0)

10+ Year Member



I plan on creating a more comprehensive write up, but I found this function to be the most effective so far for removing garbage from the "email address" field on my forms.

I would like to credit Dave Child for this function. You can search his name to find out more.

Here is his code + some additional checks…this is overkill at this point…will clean up later:

------------------------------------------------------------

//declare email variable
$email = $_POST['email'];

/***************Check 1******************/
//check for direct request to form script

if ($_SERVER['HTTP_REFERER']!= "http://www.yourdomain.com/contact-us.php") {

//kill the script if direct request to script
die("Invalid request from: " . $_SERVER['HTTP_REFERER']);

}

/***************Check 2******************/
//script from Dave Child
//check for garbage or injected mail headers in email field

function check_email_address($email) {
// First, we check that there's one @ symbol, and that the lengths are right
if (!ereg("[^@]{1,64}@[^@]{1,255}", $email)) {
// Email invalid because wrong number of characters in one section, or wrong number of @ symbols.
return false;
}
// Split it into sections to make life easier
$email_array = explode("@", $email);
$local_array = explode(".", $email_array[0]);
for ($i = 0; $i < sizeof($local_array); $i++) {
if (!ereg("^(([A-Za-z0-9!#$%&'*+/=?^_`{¦}~-][A-Za-z0-9!#$%&'*+/=?^_`{¦}~\.-]{0,63})¦(\"[^(\\¦\")]{0,62}\"))$", $local_array[$i])) {
return false;
}
}
if (!ereg("^\[?[0-9\.]+\]?$", $email_array[1])) { // Check if domain is IP. If not, it should be valid domain name
$domain_array = explode(".", $email_array[1]);
if (sizeof($domain_array) < 2) {
return false; // Not enough parts to domain
}
for ($i = 0; $i < sizeof($domain_array); $i++) {
if (!ereg("^(([A-Za-z0-9][A-Za-z0-9-]{0,61}[A-Za-z0-9])¦([A-Za-z0-9]+))$", $domain_array[$i])) {
return false;
}
}
}
return true;
}

//Check for garbage in email var...die if found.
if(!check_email_address($email)) {
die("Invalid headers supplied");
}

/***************Check 3******************/
//check for duplicate fields…this is an indication of abuse
if ( ($_POST['name']) == ($_POST['phone']) )
{
die("Duplicate Fields");
}
else{

/***************Check 4******************/
//check for blank fields

// Check for name.
$name = trim($_POST['name']);
$phone = trim($_POST['phone']);
$email = trim($_POST['email']);

//if any of the required fields are empty, then kill the script

if (empty($name) ¦¦ empty($phone) ¦¦ empty($email)) {
$name = FALSE;
$phone = FALSE;
$email = FALSE;

die("Blank Fields");
}
else{

//send form

//grab ip address of user
$hostname = $_SERVER['REMOTE_ADDR'];

//remove carriage returns and line feeds
$_POST['email'] = preg_replace("/\r/", "", $_POST['email']);
$_POST['email'] = preg_replace("/\n/", "", $_POST['email']);

mail("info@yourdomain.com","contact us form", "IP Address: ".$_POST['ipAddress']." \nDate and Time Stamp: ".$_POST['dateTime']." \n \nBegin Form \n====================================== \nName: ".$_POST['name']." \nPhone: ".$_POST['phone']." \nEmail: ".$_POST['email']." \n \nMessage:".$_POST['message']." \n \n====================================== \nEnd Form \n","From:".$_POST['email']);
header ("location: ../redirect.htm");
}

}

//end mail script

zoobie

6:00 pm on Sep 19, 2005 (gmt 0)

10+ Year Member



Sorry to say it spoofs HTTP_REFERER. That's why it uses your domain in it's code.

[edited by: zoobie at 6:02 pm (utc) on Sep. 19, 2005]

Hatty

6:00 pm on Sep 19, 2005 (gmt 0)

10+ Year Member



You can say that again, I've got one eye on this frum and one scanning another <snipped unlinked url>

Ian from Spain
#64 ¦ Wed, Aug 31, 2005 02:21 AM
OK - I've managed to tie down any field you want for any forbidden character, the code is below:

If anyone can find faults with it (I've tested and seems fine) please post back.

Ian
**PHP CODE***
$to = 'email@site.com';
$message = $_POST['message'];
$full_name = $_POST['full_name'];
$no_in_party = $_POST['no_in_party'];
$mail_list = $_POST['mail_list'];
$welcome_pack = $_POST['welcome_pack'];
$from_check = $_POST['from_check'];
$from = $_POST['from'];
$tel = $_POST['tel'];
$bcc = $_POST['bcc'];
$cc = $_POST['cc'];
clean_from($_POST);
function clean_from( &$from )
{
if(is_array($from)){
array_walk(&$from,'clean_from');
return;
} else {
$value = str_replace(array("\r","\n","Content-Type:"),"",$from);
}
}
clean_from_check($_POST);
function clean_from_check( &$from_check )
{
if(is_array($from_check)){
array_walk(&$from_check,'clean_from_check');
return;
} else {
$value = str_replace(array("\r","\n","Content-Type:"),"",$from_check);
}
}
$headers = "From: ". $_POST['from'] ."\r\n\r\n";
$message = "Additional Requests: ".$message;
$message .= "\nName: ".$full_name;
$message .= "\n\nNo in party: ".$no_in_party;
$message .= "\nMailing List: ".$mail_list;
$message .= "\nWelcome Pack: ".$welcome_pack;
$message .= "\nTel: ".$tel;
$message .= "\nEmail: ".$from;
$message .= "\nEmail check (different?): ".$from_check;

function validate_from_field($s) {
$forbidden = array('%', ',', ';', 'bcc');
foreach ($forbidden as $f)
if (strpos($s, $f)!== false) return false;
return true;
}

function validate_message($t) {
$forbidden = array('%');
foreach ($forbidden as $n)
if (strpos($t, $n)!== false) return false;
return true;
}

if (!validate_from_field($_POST['from'])) {
echo "<h4>Sorry you have entered an invalid email address, please check and try again</h4>";
echo "<a href='javascript:history.back(1);'>Click here to return</a>";
echo "<h4></h4>";
echo "<h4>If you are having problems with this form please email email@domain.com </h4>";

}
elseif (!validate_from_field($_POST['from_check'])) {
echo 'Validation failed'; // Crash and burn
}
elseif (!validate_message($_POST['message'])) {
echo '% not allowed Validation failed'; // Crash and burn
}

elseif (!preg_match("/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/", $from)) {
echo "<h4>Sorry you have entered an invalid email address, please check and try again</h4>";
}
elseif ($full_name == "") {
echo "<h4>Sorry you have not entered your name</h4>";
}
elseif ($from_check <> $from) {
echo "<h4>Please verify your email address, they are different.</h4>";
}
elseif ($bcc!= ''){
echo "System Error";
}
elseif ($cc!= ''){
echo "System error";
}
elseif
(mail($to,$subject,$message,$headers)){
echo "<h4>$full_name</h4>";
echo "<h4>Your reservation request has been sent, we will confirm back to you as soon as possible.</h4>";
}
else {
echo "<h4>Sorry we cannot send your email please try again or send an email to email@site.com </h4>";
echo "<a href='javascript:history.back(1);'>Click here to return</a>";
}

This seems to be the most comprehensive I've found so far!

[edited by: coopster at 8:33 pm (utc) on Sep. 19, 2005]
[edit reason] only authoritative urls only please [/edit]

Hatty

7:10 pm on Sep 19, 2005 (gmt 0)

10+ Year Member



I've just reread the early part of the forum and I've tried this and I hope it will work

Anders from NC
#19 ¦ Fri, Jul 29, 2005 07:46 AM
I don't know PHP, but PHP has perl-like regular expressions, so with a quick glance at the docs:

[us2.php.net...]

A default install of PHP 4.2.0 and later, you could:

$field = preg_replace( "/\n/", " ", $field );
$field = preg_replace( "/\r/", " ", $field );

Killing the \r and \n characters will be successful 100% of the time.

I don't get a - warning empty regular expression anymore - and the returned emailed information comes back in one line, so I take it it has worked but I suppose I will just have to wait and see.

Thank you all

matthijs

8:14 pm on Sep 19, 2005 (gmt 0)

10+ Year Member



aggievizzer, thanks for showing us that script, looking forward to your write up. And Hatty, I got those empt regular warnings too, if I tried that regexp without the slashes. That's the reason I tried <snipped url> another solution. The error-log mail in [the solution snippet] is handy. After upgrading all my sites with this script, one by one the error messages from different sites starting coming in, ensuring me the filter did its work ;)
However, I'm still not sure if there's no better solution. A good place to keep your eye on is the [nyphp.org ], a place were more knowledge on php security issues is shared. At the moment they give the following advice:
function safe( $name ) {
return( str_replace(array( "\\r", "\\n", "%0A", "%0a", "%0D", "%0d", "Content-Type:", "BCC:", "bcc:" ), "", $name ) );
}

[edited by: coopster at 8:47 pm (utc) on Sep. 19, 2005]
[edit reason] removed url [/edit]

twist

8:23 pm on Sep 19, 2005 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



I am no expert on this subject whatsoever, need jdMorgan for that. He always knows how to deal with issues like these. Here my $.02 on forms anyway though,

1) I write my own forms. Stops some bots.

2) All forms, when filled out correctly, will show confirmation that they have been recieved, whether or not they are actually recieved. If a person is creating a bot to attack your forms let them be lazy. Let them think that the most simple bot they created to attack your forms is working like a charm. If you let them know their bot isn't working then they only try harder. Programmers like a challenge, the harder you work to stop them, the harder they work. By telling them that their bot isn't working, your only motivating them. Knowledge is power, tell them nothing. Only you should know what messages are passing your filters.

3) All my form messages are sent to a database not to an email address. This allows me to sort and check data. This could also allow you to create all sorts of filters for your incoming messages. You could even give tolerance levels. For example, if your table is under 20 rows, let anything in, no checks. If your rows are at 100, start filtering by IP, for example, only allow 10 messages from one IP. If your rows reach 500, send yourself an email. Once again, don't let them know your messages are going into a database.

4) You could try using rand() to randomly generate your form page. You could change the name of your variables every time. You could move the positions of input boxes around at random. If a person needs to fill out name, email, and message. You could switch it(at random) to email, message, name and so on. This could also apply to the tolerance levels used in a database. If your message table has >500 rows. Change a setting in another table that would start the rand() pages. Once again, I must say, don't let the bot know the pages have become randomly generated. No person would fill out a name input box with "example@example.com" and then fill out a email box with "Joe Joe". If this happens, give the bot confirmation that the message was accepted. Even though it just ends up in oblivion.

5) Use changing images to describe your form input (like that captcha thing). Use a email image with alt="var1" for your email input. Name your inputs var1, var2 and so on. Nothing descriptive. How does the bot know what to write in the input box if it doesn't know which one is the email input box. This falls back on the rand() idea.

6) and lastly, if this person has figured out a way to make many different IP address's hit your form page, PUT UP SOME IMPRESSION ADS. A big impression banner on your form pages racking up thousands of unique IP's a day, yeah, its a little shady on your part but once they realize your making money off of their attempts to hinder you, they would probably stop right away. Ok, this last one is just a theory.

This 51 message thread spans 2 pages: 51