116 P l u g - i n P H P : 1 0 0 P o w e r S o l u t i o n s 116 P l u g - i n P H P : 1 0 0 P o w e r S o l u t i o n s TIP If you are interested in exactly how the Luhn algorithm works, there’s an explanation at http://en.wikipedia.org/wiki/Luhn_algorithm. How to Use It To verify a credit card’s details prior to submitting it to a credit card processing organization, you could use code such as this: $card = "4567 1234 5678 9101"; $exp = "06/14"; $result = PIPHP_ValidateCC($card, $exp); if (!$result) { // Ask for details again } else { // Debit the card } In the preceding example, if the card doesn’t validate, the details are re-requested, or if it does, the card is processed. CAUTION All this plug-in does is check whether the credit card details entered meet the issuer identity, checksum, and date requirements for a valid card. It should only be used as a quick test to ensure that a user has not made a typographical error when entering their details. Also you should keep yourself informed about all the latest card numbers allocated so that you can update this validator and not incorrectly reject any cards. You can keep track of the major credit card issuers at http://en.wikipedia.org/wiki/Credit_card_numbers. The Plug-in function PIPHP_ValidateCC($number, $expiry) { $ccnum = preg_replace('/[^\d]/', '', $number); $expiry = preg_replace('/[^\d]/', '', $expiry); $left = substr($ccnum, 0, 4); $cclen = strlen($ccnum); $chksum = 0; // Diners Club if (($left >= 3000) && ($left <= 3059) || ($left >= 3600) && ($left <= 3699) || ($left >= 3800) && ($left <= 3889)) if ($cclen != 14) return FALSE; // JCB C h a p t e r 6 : F o r m s a n d U s e r I n p u t 117 C h a p t e r 6 : F o r m s a n d U s e r I n p u t 117 if (($left >= 3088) && ($left <= 3094) || ($left >= 3096) && ($left <= 3102) || ($left >= 3112) && ($left <= 3120) || ($left >= 3158) && ($left <= 3159) || ($left >= 3337) && ($left <= 3349) || ($left >= 3528) && ($left <= 3589)) if ($cclen != 16) return FALSE; // American Express elseif (($left >= 3400) && ($left <= 3499) || ($left >= 3700) && ($left <= 3799)) if ($cclen != 15) return FALSE; // Carte Blanche elseif (($left >= 3890) && ($left <= 3899)) if ($cclen != 14) return FALSE; // Visa elseif (($left >= 4000) && ($left <= 4999)) if ($cclen != 13 && $cclen != 16) return FALSE; // MasterCard elseif (($left >= 5100) && ($left <= 5599)) if ($cclen != 16) return FALSE; // Australian BankCard elseif ($left == 5610) if ($cclen != 16) return FALSE; // Discover elseif ($left == 6011) if ($cclen != 16) return FALSE; // Unknown else return FALSE; for ($j = 1 - ($cclen % 2); $j < $cclen; $j += 2) $chksum += substr($ccnum, $j, 1); for ($j = $cclen % 2; $j < $cclen; $j += 2) { $d = substr($ccnum, $j, 1) * 2; $chksum += $d < 10 ? $d : $d - 9; } if ($chksum % 10 != 0) return FALSE; if (mktime(0, 0, 0, substr($expiry, 0, 2), date("t"), substr($expiry, 2, 2)) < time()) return FALSE; return TRUE; } 118 P l u g - i n P H P : 1 0 0 P o w e r S o l u t i o n s 118 P l u g - i n P H P : 1 0 0 P o w e r S o l u t i o n s Create Captcha Spam is everywhere these days, and not just in our e-mail inboxes. The net is saturated with bots (automated programs) trawling web pages in search of web forms that will let them drop their payload into a comment or other field. Usually they try to drop in a link leading to a knock-off product they are trying to sell. But worse than that, many of these bots inject pornographic links, or attempt to get users to visit phishing sites where their bank, cr edit card, or other personal details may be stolen. One of the most successful ways to prevent this is the Captcha, a type of challenge- response test used in computing to ensure that the response is not generated by a computer. The word is a highly contrived acronym that stands for Completely Automatic Public Turing Test to Tell Computers and Humans Apart. With a Captcha you are asked to re-enter some text displayed in a graphic image. If the image is complex enough, a bot will not be able to decipher it and so only human input is able to get through. This still doesn’t guarantee you will be spam-free, but with this plug-in you’ll prevent the majority of it from getting through. Figure 6-3 shows the plug-in generating a Captcha. About the Plug-in This plug-in creates a temporary image containing a word that must be typed in to verify a user is human. It returns a three-element array in which the first element is the Captcha text to be entered, the second is a unique 32-character token, and the third is the location of the Captcha image. It takes these arguments: • $size The font size of a TrueType font • $length The number of characters in the Captcha word • $font The location of a TrueType font to use • $folder The location of a folder to store the Captcha images. This must be web accessible and end with a trailing/character, or to use the current folder use a value of NULL. • $salt1 A string to make the Captcha hard to crack • $salt2 A string to make cracking even harder FIGURE 6-3 Ensuring that users are humans and not bots is easy with this plug-in. 33 C h a p t e r 6 : F o r m s a n d U s e r I n p u t 119 C h a p t e r 6 : F o r m s a n d U s e r I n p u t 119 Variables, Arrays, and Functions $file String containing the contents of the file dictionary.txt $temps Array of all words extracted from $file $temp String containing each value in turn from $temps $dict Array of all correct length words extracted from $temps $captcha String containing the Captcha word $token String containing an md5() hash based on $captcha, $salt1, and $salt2 $fname String containing the Captcha image location $image GD Library image of the Captcha image $j Loop counter PIPHP_GifText() Plug-in 19 function: converts text to a GIF image PIPHP_GD_FN1() Function used by plug-in 19 PIPHP_ImageAlter() Plug-in 14 function: modifies an image How It Works Rather than simply supplying a selection of random letters for the Captcha, I decided it’s much more natural to enter an English word, and so this plug-in requires a file called dictionary.txt to be in the same directory. This file should be a list of words with one per line and each line separated by a \r\n carriage return\linefeed pair. On the companion web site to this book at www.pluginphp.com there’s an 80,000-word dictionary.txt file already saved in the same zip file as this plug-in. Or you can choose to use your own list of words. Either way, the first thing the function does is load the contents of dictionary.txt into the variable $temps from where all the words with a length of the value in $length are extracted into the array $dict using a foreach loop, and the Captcha word is then selected from this subset of words. Next a token is created with which the Captcha can be uniquely connected. This is done by calling the PHP md5() function, which is a one-way function that converts the input into a 32-character string in such a way that the algorithm cannot be reversed. This is why it’s called a one-way function. However, instead of simply taking the dictionary word and creating an md5() hash of it, it’s necessary to obfuscate things a little. This is because some people have spent a lot of time assembling dictionaries containing the hashes of every single word. Therefore, for example, the md5() hash of the word “hello” is easily looked up and is known to be the following: 5d41402abc4b2a76b9719d911017c592 Wherever this particular hash is encountered, the chances are very high indeed that it was created from the string “hello”, and so it would be a cinch to crack this Captcha system. So, like I said, it’s necessary to be a little sneaky by making it impossible for a dictionary crack to work. 120 P l u g - i n P H P : 1 0 0 P o w e r S o l u t i o n s 120 P l u g - i n P H P : 1 0 0 P o w e r S o l u t i o n s We do this by adding additional characters to the Captcha word that only we know. Such strings of characters are called salts and this plug-in uses two of them for good measure. When you call the plug-in you will have to provide values for $salt1 and $salt2, which will be inserted on either side of the Captcha word chosen. For example, if you choose the strings 3$a7* and dk%%d, and the Captcha word is hello, then the string that will be passed to md5() is 3$a7*hellodk%%d, which results in the following hash: 99ccb37e57e885ac76b0145246ef7e8e As you can see, this is a totally different string and, without knowing the two salt values, it is utterly impossible to crack without attempting brute force (multiple attempts), which would take thousands of years, even using a modern supercomputer. So, the result of creating the hash token is placed in $token, and $fname is set to point to the location where the resulting Captcha GIF file will be stored. This is based on concatenating the value supplied in $folder, the md5() token in $token, and the file extension .gif. The function then creates the graphic image by calling PIPHP_GifText() (plug-in 19 from Chapter 5) with the correct values to form a shadowed word. This function also saves the image to disk when done. To complete the Captcha creation, the image is reloaded into memory and PIPHP_ ImageAlter() (plug-in 14 from Chapter 5) is called in four different ways (and in a couple of instances multiple times) to blur, emboss, brighten, and increase its contrast. The result is then resaved back into the GIF image and an array containing three elements is returned. These are: • The Captcha text • The md5() token • The location of the Captcha image How to Use It To create a Captcha, you call up PHP_CreateCaptcha(), passing it the required values, like this: $result = PIPHP_CreateCaptcha(26, 8, 'captcha.ttf', '', '!*a&K', '.fs£!+'); In this example, the passed values are 26 for the font size, 8 for the length of the Captcha word required, captcha.ttf for the name of a TrueType file to use, '' for the image folder to use, and !*a&K and .fs£!+ for the two salt values. You should have already uploaded a suitable TrueType font file, named captcha.ttf, to your server— preferably a nonstandard or script type font. The plug-in will return an array of three values with which you can display the Captcha image, and create a form to request the Captcha word as text input. The first of the returned values is the Captcha word itself and you don’t actually need it other than for testing purposes. So at this point let’s forget it and concentrate on the other two returned values. The first of these is the image that you need to display, like this: <img . this plug- in you’ll prevent the majority of it from getting through. Figure 6-3 shows the plug- in generating a Captcha. About the Plug- in This plug- in creates a temporary image containing a. extracted from $temps $captcha String containing the Captcha word $token String containing an md5() hash based on $captcha, $salt1, and $salt2 $fname String containing the Captcha image location $image GD. Captcha image $j Loop counter PIPHP_GifText() Plug- in 19 function: converts text to a GIF image PIPHP_GD_FN1() Function used by plug- in 19 PIPHP_ImageAlter() Plug- in 14 function: modifies an image How