When it comes to security issues, it's important to note that in addition to actual platform and operating system security issues, you also need to ensure that you write secure applications. When writing PHP applications, apply the following seven habits to ensure your application has the best possible security:
• Validate input
• Protect the file system
• Protect the database
•Protect session data
•Protect cross-site scripting (XSS) vulnerabilities
•Verify form post
•Protect against Cross-Site Request Forgeries (CSRF) Protect
Validate input
When it comes to security issues, validating your data is the most important habit you can adopt. And when it comes to input, it's very simple: don't trust the user. Your users may be excellent, and most of them may use the application exactly as expected. But wherever there is an opportunity for input, there is also a high probability of very bad input. As an application developer, you must prevent your application from accepting incorrect input. Careful consideration of the location and correct value of user input will allow you to build a robust, secure application.
Although file system and database interaction will be covered later, Listed below are general validation tips that apply to various validations :
• Use white Values in the list
• Always revalidate limited options
• Use built-in escaping functions
• Validate correct data types (like numbers)
Values in the whitelist (White- listed value) is the correct value, as opposed to the invalid black-listed value (Black-listed value). The difference between the two is that typically when validating, the list or range of possible values is smaller than the list or range of invalid values, many of which may be unknown or unexpected values.
When doing validation, remember that it is often easier to design and validate the values your application allows than to protect against all unknown values. For example, to limit a field value to all numbers, you need to write a routine that ensures that the input is all numbers. Do not write routines that search for non-numeric values and mark them as invalid when they are found.
Securing File Systems
In July 2000, a Web site exposed customer data stored in files on the Web server. A visitor to the Web site used the URL to view a file containing data. Although the file was misplaced, this example highlights the importance of protecting the file system from attackers.
If a PHP application manipulates a file and contains variable data that the user can enter, carefully check the user input to ensure that the user cannot perform any inappropriate actions on the file system. Listing 1 shows an example of a PHP site that downloads an image with a specified name.
List 1. Download file
Copy code The code is as follows:
if ($_POST['submit'] == 'Download') {
$file = $_POST['fileName'];
header("Content-Type: application/x-octet-stream ");
header("Content-Transfer-Encoding: binary");
header("Content-Disposition: attachment; filename="" . $file . "";" );
$fh = fopen($file, 'r');
while (! feof($fh))
{
echo(fread($fh, 1024));
}
fclose( $fh);
} else {
echo("<");
echo("title>Guard your filesystem" );
echo("
");
}
As you can see, the more dangerous script in Listing 1 will process all files that the web server has read access to, including files in the session directory (see "Securing session data") and even some system files ( For example /etc/passwd). For demonstration purposes, this example uses a text box where the user can type a filename, but the filename can easily be provided in the query string.
Simultaneous configuration of user input and file system access is dangerous, so it is best to design your application to use a database and hide generated filenames to avoid simultaneous configuration. However, this doesn't always work. Listing 2 provides a sample routine for validating file names. It will use regular expressions to ensure that only valid characters are used in file names, and specifically checks for dot characters: ..
Listing 2. Checking for valid filename characters
Copy code The code is as follows:
function isValidFileName($file) {
/* don't allow .. and allow any "word" character / */
return preg_match('/^(((?:.)(?!.)) |w)+$/', $file);
}
Protect database
In April 2008, the Bureau of Prisons of a certain state in the United States was querying SQL column names were used in the string, thus exposing confidential data. This breach allowed a malicious user to select which columns to display, submit the page, and obtain the data. This leak shows how users can perform input in ways that application developers could not have anticipated, and demonstrates the need to defend against SQL injection attacks.
Listing 3 shows a sample script that runs a SQL statement. In this case, the SQL statement is a dynamic statement that allows the same attack. The owner of this form may think the form is safe because they have restricted the column names to select lists. However, the code misses one last tip about form cheating — just because the code limits the options to a drop-down box doesn't mean that someone else can't post the form with the required content (including the asterisk [*]).
Listing 3. Execute SQL statement
Copy code The code is as follows:
SQL Injection Exampleif ($_POST['submit'] == 'Save') {
/* do the form processing */
$link = mysql_connect ('hostname', 'user', 'password') or
die ('Could not connect' . mysql_error());
mysql_select_db('test', $link);
$ col = $_POST['col'];
$select = "SELECT " . $col . " FROM account_data WHERE account_number = "
. $_POST['account_number'] . ";" ;
echo '
' . $select . '
';
$result = mysql_query($select) or die('
' . mysql_error() . '
' );
echo '
';
while ($row = mysql_fetch_assoc($result)) {
echo '';
echo '' . $row[$col] . ' | ';
echo '
';
}
echo '
';
mysql_close($link );
}
?>
So, to get into the habit of protecting your database, avoid using dynamic SQL code whenever possible. If you cannot avoid dynamic SQL code, do not use input directly on a column. Listing 4 shows that in addition to using a static column, you can add a simple validation routine to the account number field to ensure that the input value is not a non-numeric value.
Listing 4. Protection via validation and mysql_real_escape_string()
Copy code The code is as follows:
SQL Injection Examplefunction isValidAccountNumber($number )
{
return is_numeric($number);
}
if ($_POST['submit'] == 'Save') {
/* Remember habit #1--validate your data! */
if (isset($_POST['account_number']) &&
isValidAccountNumber($_POST['account_number'])) {
/* do the form processing */
$link = mysql_connect('hostname', 'user', 'password') or
die ('Could not connect' . mysql_error());
mysql_select_db('test', $link);
$select = sprintf("SELECT account_number, name, address " .
" FROM account_data WHERE account_number = %s;",
mysql_real_escape_string($_POST['account_number']));
echo '< p>' . $select . '';
; 🎜> echo '
';
while ($row = mysql_fetch_assoc($result)) {
echo '';
echo '' $ . row[ 'account_number']. ' | ';
$ row ['address']. '& lt;/td & gt;';
echo '& lt;/tr & gt;';
}
echo '& lt;/table & gt;'; $ link); } else {
echo "& lt; span style =" font-color: red "& gt;".
"Please support a valid account number! & lt;/span & gt;" "; }
}
?>
This example also shows the usage of mysql_real_escape_string() function. This function will filter your input correctly so it does not include invalid characters. If you have been relying on magic_quotes_gpc, you need to note that it is deprecated and will be removed in PHP V6. From now on you should avoid using it and write secure PHP applications in this case. Also, if you are using an ISP, it is possible that your ISP does not have magic_quotes_gpc enabled.
Finally, in the improved example, you can see that the SQL statement and output do not include the dynamic column options. Using this approach, you can export columns if they are added to a table that later contains different information. If you are using a framework to work with a database, your framework may already perform SQL validation for you. Make sure to check the documentation to ensure the framework is secure; if you're still unsure, verify it to make sure. Even when using a framework for database interaction, there are still additional validations that need to be performed.
Protect Sessions
By default, session information in PHP will be written to a temporary directory. Consider the form in Listing 5, which shows how to store the user ID and account number within a session.
Listing 5. Storing data in session
Copy the code The code is as follows:
< ;?php
session_start();
?>
Storing session information head>
if ($_POST['submit'] == 'Save') {
$_SESSION['userName'] = $_POST[' userName'];
$_SESSION['accountNumber'] = $_POST['accountNumber'];
}
?>
html>
Listing 6 shows the contents of the /tmp directory.
Listing 6. Session files in /tmp directory
Copy code The code is as follows:
-rw ------- 1 _www wheel 97 Aug 18 20:00 sess_9e4233f2cd7cae35866cd8b61d9fa42b
As you can see, when output (see Listing 7), the session file contains the information in a very readable format . Because the file must be readable and writable by the Web server user, session files can cause serious problems for all users on the shared server. Someone other than you could write a script to read these files and therefore try to get the values out of the session.
Listing 7. Contents of session file
Copy code The code is as follows:
userName | s:5:"ngood";accountNumber|s:9:"123456789";
Storing Passwords
Passwords should never be stored as plain text, whether in a database, session, file system, or any other form. The best way to handle passwords is to store them encrypted and compare the encrypted passwords with each other. Despite this, in practice people still store passwords in plain text. Whenever you use a website that can send passwords instead of resetting them, that means the password is stored in plain text or you can get a code for decryption (if encrypted). Even with the latter, the decryption code can be found and used.
There are two actions you can take to protect your session data. The first is to encrypt everything you put into your session. But just because encrypting your data doesn't mean it's completely secure, use this approach with caution as the only way to protect your session. An alternative is to store session data elsewhere, such as a database. You will still have to make sure to lock the database, but this approach will solve two problems: first, it will put the data in a more secure location than a shared file system; second, it will allow your application to more easily span With multiple web servers, simultaneous shared sessions can span multiple hosts.
To implement your own session persistence, see the session_set_save_handler() function in PHP. Using it you can store session information in a database or implement a handler for encrypting and decrypting all data. Listing 8 provides implemented function usage and function skeleton examples. You can also see how to use the database in the Resources section.
Listing 8. session_set_save_handler() function example
Copy code The code is as follows:
function open($save_path, $session_name)
{
/* custom code */
return (true);
}
function close()
{
/* custom code */
return (true);
}
function read($id)
{
/* custom code */
return (true);
}
function write($id, $sess_data)
{
/* custom code */
return (true);
}
function destroy($id)
{
/* custom code */
return (true);
}
function gc($maxlifetime)
{
/* custom code */
return (true);
}
session_set_save_handler("open", "close", "read", "write", "destroy", "gc");
For XSS vulnerabilities Protecting
XSS vulnerabilities represented the majority of vulnerabilities in all archived Web sites in 2007 (see Resources). An XSS vulnerability occurs when a user is able to inject HTML code into your web page. HTML code can carry JavaScript code within script tags, allowing JavaScript to run whenever the page is fetched. The form in Listing 9 could represent a forum, wiki, social network, or any other site where text can be entered.
Listing 9. Form for inputting text
Copy code The code is as follows:
< html>
Your chance to input XSS
Listing 10 demonstrates how a form that allows XSS attacks can output results.
Listing 10. showResults.php
Copy code The code is as follows:
Results demonstrating XSSecho("
");
echo("
");
echo($_POST['myText']);
echo("
?>
Listing 11 provides a basic example in which a popup Create a new window and open Google's homepage. If your web application is not protected against XSS attacks, serious damage can be caused. For example, someone could add links that mimic the site's style for the purpose of phishing (see Resources).
List 11. Sample of malicious input text
Copy code The code is as follows:
To prevent XSS attacks, as long as the value of the variable will be printed to the output , you need to filter the input through the htmlentities() function. Remember to follow Habit #1: Validate input data with values from a whitelist in inputs to your web application for names, email addresses, phone numbers, and billing information.
A more secure page showing text input is shown below.
Listing 12. More secure form
Copy code The code is as follows:
< html>
Results demonstrating XSSecho( "
You typed this:
");
echo("
");
echo(htmlentities($_POST['myText']));
echo("
");
?>
Protect against invalid posts
Form spoofing means someone sends a post to your form from an inappropriate location. The simplest way to spoof a form is to create a web page that passes all values through submission to the form. Because web applications are stateless, there is no sure-fire way to ensure that the data being published comes from a specified location. Everything from IP addresses to hostnames can be spoofed. Listing 13 shows a typical form that allows entry of information.
Listing 13. Form for processing text
Copy code The code is as follows:
< html>
Form spoofing exampleif ( $_POST['submit'] == 'Save') {
echo("
I am processing your text: ");
echo($_POST['myText']);
echo("
");
}
?>