PHP Captcha Scripts and Tutorial with Text Copying
- PHP Captcha Scripts and Tutorials
- PHP Captcha Scripts and Tutorial with Arithmetic Question
- PHP Captcha Scripts and Tutorial with Text Copying
- PHP Captcha Scripts and Tutorial with Simple Question Answering
- PHP Captcha Scripts and Tutorial with reCAPTCHA
- PHP Captcha Scripts and Tutorial with Google reCAPTCHA
PHP Captcha Scripts and Tutorial with Arithmetic Question
PHP Captcha Scripts and Tutorial with Text Copying
PHP Captcha Scripts and Tutorial with Simple Question Answering
PHP Captcha Scripts and Tutorial with reCAPTCHA
PHP Captcha Scripts and Tutorial with Google reCAPTCHA
This page is a tutorial on "PHP Captcha Scripts and Tutorial with Text Copying". Our overall method is to use the PHP GD Library and a TrueType font and PHP to make captchas.
To be clear, you do NOT need write permissions to create captcha pictures with the PHP GD library and display them and you do not need write permissions to store captcha pictures in MySQL and use our scripts to display them. (Happily, none of our captcha scripts require MySQL or any other database. However, even though the captcha aspect needs only PHP scripts and no database use, the part where the registrations store user data surely does need MySQL.) Using Google reCAPTCHA is the safest free way to do captchas, and it's popular as well as effective. But it's more fun to make your own captcha system, like the ones we supply.
The rest of this page assumes you wish to use our "text copying" captcha script, and that you are willing to use a TrueType font in the process. We dug up a TrueType font the same way you can: go to your WINDOWS folder and search. We found 389 files to choose from, such as times.ttf, comic.ttf, arial.ttf, etc. Put this and a few others in the folder your script is in so you can experiment. The hans.ttf file we found that is the Invite SF True Type Font bundled with Serif PagePlus (or get it at fonts101.com) was perfect for our PHP Captcha Scripts and Tutorial with Text Copying script, which is below. The Holisb__.ttf file we found that is the Holiday Springs BTN True Type Font (get at MyFonts.com) was perfect for our arithmetic question captcha script, found at PHP Captcha Scripts and Tutorial with Arithmetic Question.
Our PHP Captcha Scripts and Tutorial with Text Copying script uses sessions to keep track of what user is doing what. It also monitors how many login attempts the user has used and throws you out of the script after three tries. Nothing stops users from closing their browsers and returning to the script, which will start a new session and allow the user three more login attempts. But having to do that will surely slow down spambots—however easy the captchas are for humans to solve. A legitimate user will usually get the logging in process right the first time, of course, but just in case there's a typo, three tries are supported. But the script PHP Captcha Scripts and Tutorial with Arithmetic Question does NOT monitor how many login attempts the user has used. If you don't want this feature in this script, dump this:
if($_SESSION['attempts']>6){
echo '<script language="javascript">alert("Too many login attempts. Try later.");</script>';$N=1;unset($U);exit();
}else{
Also, you'll need to dump ONE of the three }}} brackets at the end of the script. It won't matter if you dump the $_SESSION['attempts'] stuff at the start of the script—it won't hurt anything.
We use the PHP GD Library and a TrueType font and PHP to create the captcha, which changes each time it is used. This type of captcha uses a string of 7 characters the user is asked to copy to prove he is human. That gives 3521614606208 combinations of text and numbers for captcha use, if you care.
Okay, let's check out the scripts:
The first one is checkid2.php. It merely starts/continues a session, then checks the sessionid. It was stored in the login2-with-captcha.php script, so if we find it set now, the session is logged so we go on. Go on where? Well, at the bottom of this page we include this checkid2.php script at the end of the captcha2-with-sessions.php script so we can send the correct captcha answer back to the login2-with-captcha.php script, since the captcha image used in that script is the captcha2-with-sessions.php script run from the image tag in login2-with-captcha.php. If you are hollering that we goofed and need to follow the sacred sessions rule of always starting them at the beginning of a script, we feel you. But it doesn't work—it merely creates errors to start a session at the start of a header('Content-Type: image/png') script, since these types of PHP pages have an even more sacred rule of always putting the image headers at the beginning of a script. So it gets down to a contest of two things that need to be first, and header('Content-Type: image/png') wins. In metaphysics, impenetrability is the name given to that quality of matter whereby two bodies cannot occupy the same space at the same time. Obviously the PHP gurus had this firmly in mind when they allowed us to start a session at the end of a PHP script (wink wink nudge nudge). The bottom line is it is the only way that worked, since we absolutely needed that variable created so our login script had access to the right captcha answer.
If the user did not visit the login script before encountering this checkid2.php script, the session will be unset and destroyed and the user will be sent to the login script.
The next script is login2-with-captcha.php, which is set up to be about logins but you could adjust it so it's about registering if the spirit moves you. It starts a session and then grabs the session id and stores it in a session variable—the same one we just tested in our checkid2.php script. Next we declare 'attempts' as a session variable via $_SESSION['attempts']=1 unless the variable is already set, in which case we increment its value by one. Each time the user has a failed login attempt, the page reloads, thereby incrementing this variable.
Next we do some JavaScript input validation of the user's entered user name and password. PHP input filtering is better, but the page is set up to not allow the user to get to the login destination—administrator-page.php—unless JavaScript is turned on. The user may go around this (?) but will run into user name checking at that page, so why bother?
Now we include the configure.php file which has the magic words that allows the MySQL database to be interacted with. Then we grab the POSTed value entry, flag, username, password, and answer. We use the strtoupper() function on the answer they give and on the correct answer returned to this script from the captcha2-with-sessions.php script. This makes their input non-case-sensitive. If you want the user to have to get the case of each of the letters correct, dump this function from the 3 places we used it. Then the 'attempts' session variable is checked and the user sees "Too many login attempts. Try later." if he has tried over 3 times, and he is exited from the script. See if you can figure out why we have if($_SESSION['attempts']>6){ instead of 3.
Once the password and username have been entered, the form that solicits these values POSTs the page right back to itself. The script then looks at whether this is the correct password and user name, and even whether or not such a user name exists. If the user has submitted the form, the $Entry flag will be set and his data will be located in the "members" table as long as this user name exists—a warning will pop up if it is not found, or if the username/password pair is invalid. Note that in checking through the "members" table for the record in which the user's user name matches the one entered into the form, we use MySQL's SELECT statement in PHP. The $N flag is set if their entry attempt is NO GOOD—which returns them to try again. $Z temporarily gets the table's users password to compare with their input.
Of course we recommend password hashing, salts, etc. but this script is just general login code focusing on captchas, not passwords. Login scripts with hashing are here: message-login.html, chat-room-login.html, forum-login.html, and blog-login.html, just to name a few.
The main point here is captchas, so we check if the answer the user gives is equal to the session variable $_SESSION['a__________a']). (Pretty cool variable name, right?) If it's correct and the user name and password match what we find in the members table, we send the user on to the administrator-page.php. How? And where? Simple—we use an extra form for that, which POSTs the user name and gets submitted via JavaScript. The administrator-page.php script is the action URL of that form.
So, "formpw" (its name) is the form that receives the user input for logging in. Note the hidden fields that are used to POST flags to the PHP POST function, requiring a page reload. It's very convenient that forms can send data to their own pages like this, since JavaScript cannot send data to PHP without POST, GET, or cookies. PHP, on the other hand, can send anything to JavaScript, on the same page as the PHP, with the use of JSON, or if it's simple numbers, without need of JSON. Note that JSON was used to get the POSTed username converted from its PHP variable $U. This value was stuck in the hidden field named username in the form named "MyForm" before submission.
Note the links to pages to help you recover lost user names or passwords. It's important to give users options if they forget something, and this automatic way requires no administrative work at your end, answering emails with "forgot password" stories of woe. Our site has scripts for not just that but tons more, if you look. There are links to http://www.css-resources.com/contact.html and http://www.css-resources.com/Administrator-PHP-Code-for-Multiple-PSB-Hosts.html which you will want to change to your own links. The link to "../index.html" is just a guess about where your Home page might be. Tweak it as needed.
Note that the captcha image used in this script is the captcha2-with-sessions.php script run from the image tag in login2-with-captcha.php:
<div style='margin:0px 0 0 30px'>
<IMG SRC="captcha2-with-sessions.php" alt='captcha'>
</div>
It's nice to know that the HTML gurus don't mind a PHP script instead of an image file whose extension is png, jpg, or gif. It's sure convenient!
The next script was fun to write but we must confess that the "sessions at the end of the script" necessity threw us for quite a while! Who knew? The header('Content-Type: image/png') declaration limits what you can do in your PHP scripting as long as the created image is among the living. So, nuts to sessions and the echo() function while the image's heart is still beating. Once you drive a stake through it, you can program normally. Echo() is about outputting text to the browser screen. PHP doesn't like to see that you are outputting text if you told it you're outputting an image. But once the image is kaput, you may output normally. There may be a few restrictions because of that header, but sessions (after image destruction) is not one of them.
In the script, we are trying for a random text captcha. So we use the PHP functions range() and array_merge(). The first one builds an array with the range of characters in the parameters, and the second one merges all the arrays into one big array called $aZ09. Next we use the PHP function mt_rand() to get 7 random characters from our large alphanumeric array. We use the count() function since we're too lazy to figure out the simple arithmetic of adding the number of array elements and coming up with an obvious 62. (Oops! Guess we are not too lazy after all!) Then we build the captcha challenge and put this result in $b.
In informing PHP about our font choice, $font = 'hans.ttf' is used. It's the Invite SF True Type Font bundled with Serif PagePlus, (or get it at fonts101.com). This assumes you loaded the font into the folder your script is in. We use the PHP GD Library function imagecreatetruecolor() after defining its size. Then we use the function imagecolorallocate() to define some RGB colors. We fill the image with gray using imagefill(). Then we use imageline() to draw a border. Next we use imagettftext() to draw the captcha text into the image using TrueType fonts. We output the image with imagepng(), then kill it in memory—but not its output in the browser—with imagedestroy().
We could have mushed up the image a bit more to confuse the bots but we settled for a red shadow. You could use several shadows of colors closer to black. But the best option would be using a different font for each of the seven characters, if you're truly worried about spambots.
We start up sessions, stick our correct captcha answer in our session variable $_SESSION['a__________a'], and that's it. The image is on the screen and out of memory and the correct answer is now available to the login2-with-captcha.php script via that session variable, which we will be comparing with the user's input. So here are the three scripts:
The first script needed is: checkid2.php
<?php
session_start();
if(!isset($_SESSION['sessionid'])){
session_unset();
session_destroy();
header('location: login2-with-captcha.php');
}else{
// session logged
}
?>
The second script needed is: login2-with-captcha.php
<?php
session_start();
$_SESSION['sessionid'] = session_id();
if(isset($_SESSION['attempts'])){
$_SESSION['attempts']=$_SESSION['attempts']+1;
}else{
$_SESSION['attempts']=1;}
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<HTML>
<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1252">
<TITLE>Login</TITLE>
<meta name="description" content="Login Script">
<meta name="keywords" content="Login Script,login,php,javascript, dhtml, DHTML">
<style type="text/css">
BODY {margin-left:0; margin-right:0; margin-top:0;text-align:left}
p, li {font:13px Verdana; color:black;text-align:left}
h1 {font:bold 28px Verdana; color:black;text-align:center}
h2 {font:bold 24px Verdana;text-align:center}
h3 {font:bold 15px Verdana;}
</style>
<script language="javascript">
function validatepassword(){
var ck_password = /^[A-Za-z0-9!@#$%^&*()_]{4,20}$/;
if (document.formpw.password.value.search(ck_password)==-1)
{alert("Please only enter letters, numbers and these for password: !@#$%^&*()_");return false}
var ck_username = /^[A-Za-z0-9_]{1,20}$/;
if (document.formpw.username.value.search(ck_username)==-1)
{alert("Please only enter letters, numbers and underline for user name.");return false}
return true;}
</script>
</head>
<body>
<?php
include_once"configure.php";
$Entry=$_POST['entry'];
$FLAG=$_POST['flag'];
$U=$_POST['username'];
$P=$_POST['password'];
$A=strtoupper($_POST['answer']);
$N=0;
$Z='';
$_SESSION['username'] = $U;
if($_SESSION['attempts']>6){
echo '<script language="javascript">alert("Too many login attempts. Try later.");</script>';$N=1;unset($U);exit();
}else{
if($Entry==1 && $A<>strtoupper($_SESSION['a__________a'])){
echo '<script language="javascript">alert("Wrong captcha answer. Please try again.");window.location="login2-with-captcha.php";</script>';$N=1;unset($U);
}else{
if($Entry==1 && $A==strtoupper($_SESSION['a__________a'])){
$check_user_data = mysql_query("SELECT * FROM members WHERE username = '$U'") or die(mysql_error());
if(mysql_num_rows($check_user_data) == 0)
{echo '<script language="javascript">alert("This user name does not exist. Please try again.")</script>';$N=1;unset($U);}
else {$get_user_data = mysql_fetch_array($check_user_data);}
}
if($N==0 && $Entry==1){$Z=$get_user_data['admin_password'];}
if($Z != $P && $Entry==1)
{echo '<script language="javascript">alert("Username/password pair is invalid. Please try again.")</script>';$N=1;unset($U);}
if(($N==1||$Entry==0) && empty($FLAG)){ ?>
<h1>Login</h1>
<div id='pw' style='position:absolute;top:210px;left:200px;width:300px'><table style="background-color:#8aa;border-color:#00f" border='6' cellspacing=0 cellpadding=6><tr><td>
<form id='formpw' name="formpw" method="post" action="login2-with-captcha.php" onsubmit="return validatepassword()">
<label for="User Name"><b>User Name: </b><input type="text" name="username" size="20" maxlength="30" value=""></label>
<label for="Password"><b>Password: </b><input type="password" name="password" size="20" maxlength="20" value=""></label><br><br>
<input type="hidden" name="flag" value="0">
<input type="hidden" name="entry" value="1">
<div style='margin:0px 0 0 30px'>
<IMG SRC="captcha2-with-sessions.php" alt='captcha'>
</div>
<label for="Captcha answer"><b>Captcha answer: </b><input type="text" name="answer" size="20" maxlength="20" value=""></label>
<input type="submit" value="Submit">
<input type="reset" value="Reset"></form></td></tr></table>
</div>
<div id='pw' style='position:absolute;top:210px;left:570px;width:360px'><BR>
<a href="../index.html">Home</a><BR>
<a href="http://www.css-resources.com/contact.html">Contact us</a><BR>
<a href='forgot-password.php'>I forgot my password</a><BR>
<a HREF='forgot-user-name.php'>I forgot my user name</a><BR>
<a HREF="http://www.css-resources.com/Administrator-PHP-Code-for-Multiple-PSB-Hosts.html">Administrator PHP Code for Multiple PSB™ Hosts</a>
</div>
<?php
} else {
?>
<form name="MyForm" method="POST" action="administrator-page.php">
<input type="hidden" name="username" value=" ">
</form>
<script language="javascript">
var u = <?php echo json_encode($U); ?>;
document.MyForm.username.value=u;
document.MyForm.submit();
</script>
<?php
}}}
?>
The third script needed is: captcha2-with-sessions.php
<?php
header('Content-Type: image/png');
$aZ09 = array_merge(range('A', 'Z'), range('a', 'z'),range(0, 9));
$b='';
for($c=0;$c<7;$c++){
$m=$aZ09[mt_rand(0,count($aZ09)-1)];
$b=$b.$m;}
$wide = 200;
$high = 31;
$picture = imagecreatetruecolor($wide,$high);
$gray = imagecolorallocate($picture,223,223,223);
$red = imagecolorallocate($picture,255,128,128);
$black=imagecolorallocate($picture, 0, 0, 0);
imagefill($picture,0,0,$gray);
imageline($picture, 0, 0, 0, $high, $black);
imageline($picture, 0, 0, $wide, 0, $black);
imageline($picture, $wide-1, 0, $wide-1, $high-1, $black);
imageline($picture, 0, $high-1, $wide-1, $high-1, $black);
$font='hans.ttf';
//Invite SF True Type Font (get with Serif PagePlus or at fonts101.com)
imagettftext($picture, 20, 0, 29, 27, $red, $font, $b);
imagettftext($picture, 20, 0, 26, 24, $black, $font, $b);
imagepng($picture);
imagedestroy($picture);
include_once"checkid2.php";
$_SESSION['a__________a'] = $b;
?>