Protecting a site against bot programs
Posted: Sat May 04, 2013 9:12 pm
After dealing with a hacker attack on one of my domains for more than a week (the attacks have ceased, so either I fixed it or he got tired of the game), and receiving some very useful comments and suggestions from others on this and another forum, I have had some thoughts of my own about protective measures that might be (should be?) taken to reduce the exposure of database entry forms to robot programs. I must begin by stating that I am not a web security expert. I have created quite a few web interfaces for databases and am an experienced programmer in PHP, Javascript, and the web page protocols, but this approach that I'm going to describe is just something that seems to me to offer some protection, it's not the result of any in-depth security experience. Indeed, I hope that some real security gurus will critique my suggestion and point out flaws in my reasoning or execution.
So what I am trying to protect against is the kind of a bot program that scans your HTML and recognizes a form element, obtains the action script path and filename, then recognizes each input element and its name, then it will perhaps select some or all of your form fields and construct an HTTP POST request to be sent to the action script with its own malicious data. I used to think that a decent CAPTCHA element would pretty well protect against this kind of attack, but I've now been advised that most CAPTCHA methods have been compromised by hackers, and that seems to be what happened in my recent case. There are certainly several useful techniques that will strengthen your security; you could check for IP addresses that are on the DNS Blacklists (although if botnets are being used, the "innocent" host computers that send out these are generally not on any black list yet); you can validate data, both on the client side and especially on the server side, but this isn't always very effective, depending on what kind of data fields you expect from a legitimate human filling in the form. I'm sure there are others, as well. But I came up with the following idea.
In the case of such a bot program. what if your form script submits its form using a Javascript onSubmit() function that manipulates your form data and field names before sending the HTTP POST request? Since we're protecting against a robot program, not a human hacker, it's not important that a human could read your Javascript code and figure out what you are doing, because there is unlikely to be a human scanning your code. Here is an example of what you might do.
Basically, instead of having a "submit" type Input element in your form, just have a "button" type that calls your own "submit" function in its onClick event, in which you perform any data validation you need to do, then switch around some of the values in the form fields, including at least one "hidden" form field, prior to issuing the Javascript command to submit the form. This means that your PHP action script (perhaps the same script as contains your form) can test for these manipulated values in the $_POST array. If they don't correspond to what you expect, you know that this input did NOT result from someone using your data entry script and its Javascript custom submit function.
It's a fairly simple thing to do. What have I missed? I know the code works, but how effective is it?
So what I am trying to protect against is the kind of a bot program that scans your HTML and recognizes a form element, obtains the action script path and filename, then recognizes each input element and its name, then it will perhaps select some or all of your form fields and construct an HTTP POST request to be sent to the action script with its own malicious data. I used to think that a decent CAPTCHA element would pretty well protect against this kind of attack, but I've now been advised that most CAPTCHA methods have been compromised by hackers, and that seems to be what happened in my recent case. There are certainly several useful techniques that will strengthen your security; you could check for IP addresses that are on the DNS Blacklists (although if botnets are being used, the "innocent" host computers that send out these are generally not on any black list yet); you can validate data, both on the client side and especially on the server side, but this isn't always very effective, depending on what kind of data fields you expect from a legitimate human filling in the form. I'm sure there are others, as well. But I came up with the following idea.
In the case of such a bot program. what if your form script submits its form using a Javascript onSubmit() function that manipulates your form data and field names before sending the HTTP POST request? Since we're protecting against a robot program, not a human hacker, it's not important that a human could read your Javascript code and figure out what you are doing, because there is unlikely to be a human scanning your code. Here is an example of what you might do.
Basically, instead of having a "submit" type Input element in your form, just have a "button" type that calls your own "submit" function in its onClick event, in which you perform any data validation you need to do, then switch around some of the values in the form fields, including at least one "hidden" form field, prior to issuing the Javascript command to submit the form. This means that your PHP action script (perhaps the same script as contains your form) can test for these manipulated values in the $_POST array. If they don't correspond to what you expect, you know that this input did NOT result from someone using your data entry script and its Javascript custom submit function.
Code: Select all
<!DOCTYPE HTML>
<html>
<head>
<title>Test</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF8" />
<style type='text/css'>
.REQD {
border:1px solid red;
}
</style>
<script type='text/javascript'>
function myreset(field) {
if(field.value!='') field.style.borderColor='green'
}
function mysubmit() {
if(document.myform.nm.value=='' || document.myform.pw1.value==''
|| document.myform.pw2value=='' || document.myform.yb.value=='') {
alert("You didn't complete all the fields")
} else if(document.getElementById('pw2').value != document.getElementById('pw1').value) {
alert("Your 2 passwords must match")
} else if(document.getElementById('yb').value < 1900 || document.getElementById('yb').value > 2000) {
alert("Invalid year of birth")
} else {
/* Here is where the entered value for 'yb' gets shifted to the hidden input 'val' */
document.getElementById('val').value = document.getElementById('yb').value
/* and is replaced by 'OK' */
document.getElementById('year').value = 'OK'
/* Now submit the form */
document.forms['myform'].submit()
/* You may want to redirect to another page here, so as not to confuse the user */
alert("Form submitted")
}
}
</script>
</head>
<body>
<?php
if(isset($_POST['nm'])) {
/* If any field is blank, set its value to "BAD" */
$name = isset($_POST['nm']) ? $_POST['nm'] : "BAD";
$pwd = isset($_POST['pw1']) ? $_POST['pw1'] : "BAD";
/* Note that the actual year data should NOT be in the 'year' field, but in the 'val' field */
$birth = isset($_POST['val']) ? $_POST['val'] : "BAD";
$val = isset($_POST['year']) ? $_POST['year'] : "BAD";
echo "<br />";
$sql = "INSERT INTO mydata (nm, pw, yob) VALUES ('$name', '$pwd', '$birth') LIMIT 1";
echo "<br />$sql";
/* connect to database, execute SQL INSERT statement */
echo "<br />Database updated.";
} else {
?>
<form name='myform' id='myform' method='post' action=''>
<div>Enter your name: <input type='text' class='REQD' name='nm' id='nm' onBlur='myreset(this);' onChange='myreset(this);' /></div>
<div>Enter your email address: <input type='password' class='REQD' name='pw1' id='pw1' onBlur='myreset(this);' onChange='myreset(this);' /></div>
<div>Repeat the email address: <input type='password' class='REQD' name='pw2' id='pw2' onBlur='myreset(this);' onChange='myreset(this);' /></div>
<div>What year were you born? <input type='text' class='REQD' name='year' id='year' onBlur='myreset(this);' onChange='myreset(this);' /></div>
<input type='hidden' name='val' id='val' value='BAD' />
<div><input type='button' name='sub' id='sub' value='Submit' onClick='mysubmit();' /></div>
</form>
</body>
</html>
<?php
}
?>