Back to examples |
** Update: Code has been fixed .. sorry for the bug
You may have a specfic page on your site which you want to restrict to "human" access. This meaning that you don't want an automated script reading your page. You may also want to stop over-access to a certain page, maybe you want to prevent people from extracting all the information from an online database by repetative refreshes or URL modification. The best example for what we are trying to acheive in this example is the WHOIS lookup on the Network Solutions site ( it is on the page after the one linked to here ).
A good part of this code has already been listed on this site in the hidden email code. We are using the same encyption code as that example and pretty much the same principles - using GD to display the verification code with a function that is passed encrypted data.
The first variation is in the GD display routine. To prevent the use of OCR to "read" the PNG ( or JPG) image we try to "fussy" up the background and some of the foreground. We just create a set of verticle and horizontal line drawn in various shades of gray. Of course, we are not saying this CAN'T be read by OCR, just that it makes it very difficult to get a reliable conversion. The other thing you may notice is that the 4 digits display are extracted from a pool of letters and numbers. Some letters and numbers a missing from this list. The reason for this it that certain characters tend to look the same once we have distorted the final image. This can lead to confusion for your potential end user. The best example for this would be I,i,L,l,J and 1 - even at the best of times these letters look the same!
The other part is the validation itself. The initial page contains a URL to the image displaying the code, plus the encrypted version of the code within the form. The public key for the encrytion is set to the current time. This means that the same 4 letter combination will never have the same encrypted key.
When the form is POSTed, we decode the encrypted version with our private key, and compare it against the inputed text. If they match - hey presto, you have been validated. From that point you can do what you want, either redirect to another page or being a session somewhere.
As stated in the other article, I do not claim ownership for the basic encryption / decryption class - someone with a lot more brains than I created that part of the code.
Addtion
A slight addition to the code in the demo ( downloadable from the link below ) is the time-stamping of the code. This is done to prevent a script from hard-passing values repeatedly, ie an end-user works out what one encrypted code is and then passes the same value all the time. The timestamp allow for a code to have an "age". In the demo example we set this as 2 minutes. Any attempt to use the code after 2 minutes results in an error.
To perform this we add a UNIX timestamp to the end of the code ( obtained by using the time() function ), delimited by an underscore. The un-encrpted code therefore looks like xxxx_nnnnnnn, with xxxx being the 4 character code and the nnnnnnn being the number of seconds since 1972. When displaying or decoding, explode() the decrypted value and check the code in the first array element against your source code, and subtract the time ( in the second element ) from the current system time value obtained via time(). Do some simple math to get the number of seconds the code has been active for.
You could also add any sort of user data to the encypted string to further enhance the system. For instance if dealing with whois queries, first obtain the domain name / IP, then use that string as part of the encryption process ( perhaps encode as xxxx_nnnnnnn_domain. This would mean the code is only valid for 1 domain name within a 2 minute period. |
<?php
class cls_encrypt { var $_key;
function cls_encrypt() {
// This is your private key. Set to something else // when using in your site.
$this->_key = "php_example"; return 1; }
function keyED($txt) { $encrypt_key = md5($this->_key); $ctr=0; $tmp = ""; for ($i=0;$i<strlen($txt);$i++) { if ($ctr==strlen($encrypt_key)) $ctr=0; $tmp.= substr($txt,$i,1) ^ substr($encrypt_key,$ctr,1); $ctr++; } return $tmp; }
function html_encrypt ($txt) { return urlencode($this->encrypt($txt)); }
function encrypt($txt) { // Public key, which we always set to a unique value.
$encrypt_key = md5(microtime()); $ctr=0; $tmp = ""; for ($i=0;$i<strlen($txt);$i++) { if ($ctr==strlen($encrypt_key)) $ctr=0; $tmp.= substr($encrypt_key,$ctr,1) . (substr($txt,$i,1) ^ substr($encrypt_key,$ctr,1)); $ctr++; } return $this->keyED($tmp); }
function html_decrypt($txt) { return $this->decrypt(urldecode($txt)); }
function decrypt($txt) { $txt = $this->keyED($txt); $tmp = ""; for ($i=0;$i<strlen($txt);$i++) { $md5 = substr($txt,$i,1); $i++; $tmp.= (substr($txt,$i,1) ^ $md5); } return $tmp; } }
// Similar to the email hiding code, we are using the same script // for displaying the image as well as handling the encryption. // You could easily seperate out the two parts of the code.
// So, are we being called by the <IMG> tag? We can tell because // we are passed a different variable name - $img_code.
$img_code = $HTTP_GET_VARS["img_code"]; if(!empty($img_code)) { $enc = new cls_encrypt;
// We add spaces just for padding
$img_code = " ".$enc->html_decrypt($img_code)." ";
// Change this to whatever you want. 5 is just a nice size
$font = 5;
$height = imagefontheight($font); $width = imagefontwidth($font)*strlen($img_code); $im = imagecreate($width,$height); $im_final = imagecreate($width*2,$height*2);
$gray = imagecolorallocate($im, 0x1f, 0x1f, 0x1f); $white = imagecolorallocate($im, 0xFF, 0xFF, 0xFF);
// Our shades of grey
$rwhite[0] = imagecolorallocate($im_final, 0xcf, 0xcf, 0xcf); $rwhite[1] = imagecolorallocate($im_final, 0xdf, 0xdf, 0xdf); $rwhite[2] = imagecolorallocate($im_final, 0x9f, 0x9f, 0x9f);
imagefill($im,0,0,$white); imagestring($im, $font,0,0,$img_code,$gray);
// We increase the image size as even the "5" font is a bit too small // for me. We just double the height and width.
imagecopyresized( $im_final, $im, 0,0,0,0, $width*2,$height*2, $width,$height);
// Draw the "fuzzy" background.
for($y=0;$y<$height*2;$y+=6) { imageline($im_final,0,$y,$width*2,$y,$rwhite[rand(0,2)]); }
for($x=0;$x<$width*2;$x+=6) { imageline($im_final,$x,0,$x,$height*2,$rwhite[rand(0,2)]); }
// Return as PNG. Returning as a JPG may make the image even more fuzzy. // Try it, you may like it.
header("Content-type: image/png"); imagepng($im_final); imagedestroy($im_final); imagedestroy($im); } else {
// Otherwise we are validating if we have POSTed $check_code or are just // generating the initial code.
$check_code = $HTTP_POST_VARS["check_code"];
$validated = 0;
if(!empty($check_code)) { $enc = new cls_encrypt;
// $hidden_code is the encrypted 4 digit code
$hidden_code = $enc->html_decrypt($hidden_code);
// If the decrypted version matches the inputted text, // we are good to go!
if($hidden_code==$check_code) { $validated=1; } else { $validated=-1; } }
// If we have been validated or are starting from the // beginning, generate the 4 digit code
if($validated<>1) {
// This is our character pool, with confusing letters removed
$chars = "ABCDEFGHJKMNPRSTUVWXYZabcdefghzkmnprstuvwxyz23456789"; $len = strlen($chars)-1; for($i=0;$i<4;$i++) { $show_code .= substr($chars,rand(0,$len),1); }
// And we encrypt it
$enc = new cls_encrypt; $show_code = $enc->html_encrypt($show_code); } }
*/ Your HTML FORM will look something like this:
<input name="check_code" type="text" class="PHPSAMPLE" id="check_code" value="" size="10" maxlength="4"> <input name="hidden_code" type="hidden" class="PHPSAMPLE" id="hidden_code" value="<?php print(urlencode($show_code));?>"> <input name="Submit" type="submit" class="PHPSAMPLE" value="Validate"> <img src="<?php print($PHP_SELF);?>?img_code=<?php print(urlencode($show_code));?>">
*/
?>
|
|
|
misc_9.zip
|
|
|
|