Validation can be done not only on data types but also on other aspects of the user input. However, the type validation is more of a security concern than the actual meaning of the value in your application context. For example, say the user fills out a form field called “age” with a value of 3000. Because we have yet to find a person anywhere (on Earth or anywhere else) who lived 3,000 years, the age value is invalid. However, it isn’t an invalid data type. In this case, you may want to com- bine all your validity checking in a single isValidAge() function. One of the best ways to validate data is to use regular expressions. Following are some of the regular expression functions PHP provides: ◆ preg_match(). This function takes a regular expression and searches for it in the given string. If a match is found, it returns TRUE; otherwise, it returns FALSE. The matched data can also be returned in an array. It stops searching after finding the first match. For example, say you want to find out if a user data field called $userData contains anything other than digits. You can test it with preg_match(“/[^0-9]/”, $userData). Here, the regular expression /[^0-9]/ tells preg_match to find anything but the digits. ◆ preg_match_all(). This function is just like preg_match(), except it continues searching for all regular expression patterns in the string. For example: preg_match(“/[^0-9]/”, $userData, $matches). Here the regular expression /[^0-9]/ tells preg_match_all to find anything but the digits and store them in $matches. ◆ preg_quote(). You can use this function to escape a regular expression that contains regular expression characters. For example, say you want to find out if a string called $userData contains a pattern such as “[a-z]”. If you call the preg_match() function as preg_match(“/[a-z]/”, $userData) , it will return wrong results because “[a-z]” happens to be a valid regular expression itself. Instead, you can use preg_quote() to escape “[a-z]” and then use it in the preg_match() call. For example, preg_match(‘/’ . preg_quote(“[a-z]”) . ‘/’ , $userData) will work. There are other functions such as preg_grep(), preg_replace(), and so forth, that are also useful. For example, you can access information on these functions via http://www.evoknow.com/preg_grep and http://www.evoknow.com/ preg_replace . Instead of writing validation routines for common data types, you can find free validation classes on the Web. One such class is called Validator, which can be found at www.thewebmasters.net/php/Validator.phtml. After you download and install the Validator class per its author’s instructions, you can use it very easily. For example, Listing 2-5 shows a simple Web form script called myform.php that uses this validation class to validate user data. 36 Part I: Designing PHP Applications 04 549669 ch02.qxd 4/4/03 9:24 AM Page 36 Listing 2-5: myform.php <?php error_reporting(E_ALL); define(‘DEBUG’, FALSE); include(“class.Validator.php3”); // Create a Validator object $check = new Validator (); // Get User data $email = (! empty($_REQUEST[‘email’])) ? $_REQUEST[‘email’] : null; $state = (! empty($_REQUEST[‘state’])) ? $_REQUEST[‘state’] : null; $phone = (! empty($_REQUEST[‘phone’])) ? $_REQUEST[‘phone’] : null; $zip = (! empty($_REQUEST[‘zip’])) ? $_REQUEST[‘zip’] : null; $url = (! empty($_REQUEST[‘url’])) ? $_REQUEST[‘url’] : null; DEBUG and print “Debug Code here \n”; // Call validation methods if (!$check->is_email($email)) { echo “Invalid email format<br>\n”;} if (!$check->is_state($state)) { echo “Invalid state code<br>\n”; } if (!$check->is_phone($phone)) { echo “Invalid phone format<br>\n”;} if (!$check->is_zip($zip)) { echo “Invalid zip code<br>\n”; } if (!$check->is_url($url)) { echo “Invalid URL format<br>\n”; } // If form data has errors show error and exit if ($check->ERROR) { echo “$check->ERROR<br>\n”; exit; } // Process form now echo “Form processing not shown here.<br>”; ?> The class Validator.php3 is included in the script. The $check variable is a Validator object, which is used to validate user-supplied data. If there is any error in any of the validation checks — that is, if any of the validation methods return false — the script displays an error message. If no error is found, the script continues to process the form, which is not shown in this sample code. To learn more about the validation methods that are available in this class, review the documentation supplied with the class. Chapter 2: Understanding and Avoiding Security Risks 37 04 549669 ch02.qxd 4/4/03 9:24 AM Page 37 Not Revealing Sensitive Information Another major source of security holes in applications is unnecessary disclosure of information. For example, say you have a script called mysite.php as follows: <?php phpinfo(); ?> This script shows all the PHP information about the current site, which is often very useful in finding various settings. However, if it is made available to the public, you give malicious hackers a great deal of information that they would love to explore and potentially exploit. Such a harmless script can be a security hole. It reveals too much information about a site. For security purposes, it is extremely important that you don’t reveal your system-related information about your site to anyone. We recommend that you use phpinfo() in only development systems which should not be allowed to be accessed by everyone on the Web. For example, you can use $_SERVER[‘REMOTE_ADDR’] value to restrict who has access to a sensitive script. Here is an example code segment: <?php // Enable all error reporting error_reporting(E_ALL); // Create a list of valid IP addresses that can access // this script $validIPList = array(‘192.168.1.1’, ‘192.168.1.2’); // If current remote IP address is not in our valid list of IP // addresses, do not allow access if (! in_array($_SERVER[‘REMOTE_ADDR’], $validIPList)) { echo “You do not access to this script.”; exit; } // OK, we have a valid IP address requesting this script // so show page phpinfo(); ?> 38 Part I: Designing PHP Applications 04 549669 ch02.qxd 4/4/03 9:24 AM Page 38 Here the script exists whenever a request to this script comes from a remote IP address that is not in the valid IP list ($validIPList). Let’s take a look at some other ways in which you can safely conceal informa- tion about your application: ◆ Remove or disable any debugging information from your application. Debugging information can provide clues about your application design (and possibly its weaknesses) to others who may take the opportunity to exploit them. If you add debugging code, use a global flag to enable and disable debugging. For example: <?php define(‘DEBUG’, FALSE); DEBUG and print “Debug message goes here.\n”; ?> Here DEBUG constant is set to FALSE and, therefore, the print statement is not going to print anything. Setting DEBUG to TRUE enables debug mes- sages. If all your debug code is enabled or disabled in this manner, you can easily control DEBUG messages before you put the script in the pro- duction environment. ◆ Don’t reveal sensitive paths or other information during Web-form processing. A common misunderstanding that hidden fields are secret, often causes security-novice developers to reveal sensitive path or other information during Web-form processing. For example: <input type=hidden name=”save_path” value=”/www/secret/upload”> This line in a HTML form is not hidden from anyone who has a decent Web browser with the View Source feature. So do not ever rely on hidden field for security. Use hidden fields only for storing information that are not secret. ◆ Never store sensitive information on the client side. If you must store sensitive data, consider using a database or at least a file-based session, which will store data on the server side. If you must store data on the client side for some special reason, consider encrypting the data (not just encoding it). See Chapter 22 for details on data encryption. Chapter 2: Understanding and Avoiding Security Risks 39 04 549669 ch02.qxd 4/4/03 9:24 AM Page 39 Summary In this chapter, you learned about the common security risks for PHP applications and how to deal with them. Most of the security risks are related to user input and how you handle them in your scripts. Expecting all users will behave politely and will not try to break your code is not at all realistic. Let’s face it, there are a lot of people (of all ages) with too much free time and Internet bandwidth these days, which means there is a lot out there with intents to hack, deface Web sites just for the sake of it. So do not trust user input to be just what you need to run your appli- cation. You need to deal with unexpected input as well. Revealing sensitive information such as software version, server environment data, etc., can also have a major ill effect on your overall security as such informa- tion can be used in building attack tools or techniques. The best practice is to reveal as little as necessary. 40 Part I: Designing PHP Applications 04 549669 ch02.qxd 4/4/03 9:24 AM Page 40 . script called myform .php that uses this validation class to validate user data. 36 Part I: Designing PHP Applications 04 549669 ch02.qxd 4/4/03 9:24 AM Page 36 Listing 2-5: myform .php < ?php error_reporting(E_ALL); define(‘DEBUG’,. of information. For example, say you have a script called mysite .php as follows: < ?php phpinfo(); ?> This script shows all the PHP information about the current site, which is often very useful. script.”; exit; } // OK, we have a valid IP address requesting this script // so show page phpinfo(); ?> 38 Part I: Designing PHP Applications 04 549669 ch02.qxd 4/4/03 9:24 AM Page 38 Here the script exists