Forum Moderators: coopster
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.
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.
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...
then again, this could all be a 'pie in the sky' solution! for some reason, i doubt it works the way i just described.
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);
?>
[edited by: coopster at 3:50 pm (utc) on Sep. 19, 2005]
[edit reason] removed url per TOS [webmasterworld.com] [/edit]
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.
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']);
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]
I think Rons right as there's also some unreliability with the preg_replace and preg_match being used.
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
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]
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
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]
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.