Security should be a top concern throughout the development of any PHP web application. There are some very simple measures you can take to protect your application from potential abuse. This post will cover some of the basics of PHP security.
I do not consider myself a PHP security expert, but these are things that every developer should know. Also keep in mind that security is a process and not a result.
1.Input Filtering
Assume everything is dirty until proven clean. Filtering all data from external sources is probably the most important security measure you can take. This can be as easy as running some simple built-in functions on your variables.
When it comes to accepting user input, never directly use anything in $_GET or $_POST. Check each value to make sure it is something expected and assign it to a local variable for use.
// input filtering examples
Make sure it is an integer
$
/
/integer = intval($_POST['variable_name']);
// Make it safe to use in a URL
riable_name']);
$url_string = urlencode($_POST['v
a
You can also check a value against a list of acceptable values. Here are two methods of doing this:
// input filtering examples
age = 'home'; // initialize the variable
$
p
Check input against a white-list of known options
$
/
/valid_options = array();
$valid_options[] = 'home';
bout';
if(in_array($_GET['pag
$valid_options[] = 'downloads';
$valid_options[] = '
ae'], $valid_options))
{
$page = $_GET['page'];
}
// OR this also works
$page = $_GET[
switch($_GET['page'])
{
case 'home':
case 'downloads':
case 'about'
:'page'];
break;
}
PHP as of version 5.2 provides a set of filtering functions designed just for the purpose of filtering user data. The filter_input() function is used to access a filtered version of input variables. This way you never have to touch the raw input via the $_GET or $_POST arrays.
// filter_input examples
Make sure it is an integer
$
/
/integer = filter_input(INPUT_POST, 'variable_name', FILTER_SANITIZE_NUMBER_INT);
// Make it safe to use in a URL
OST, 'variable_name', FILTER_SANITIZE_ENCODED);
// Make sure it is a valid URL
$url_string = filter_input(INPUT_
P$url = filter_input(INPUT_POST, 'variable_name', FILTER_VALIDATE_URL);
2.Output Filtering
It is also important to filter what comes out of your applications. You want to avoid outputting the wrong characters and breaking the page rendering. This is also important in order to block certain attacks involving JavaScript injected by malicious users. There are a few functions to know for cleaning up text to display to the user:
- htmlspecialchars(): Converts special HTML characters to entities
- htmlentities(): Converts all possible characters to HTML entities
- strip_tags(): Remove all HTML tags from a string (you can also selectively allow tags using the second optional parameter)
echo htmlspecialchars($text); // <a href="test">Test</a>
echo strip_tags($text); // Test
3.Database Queries
If your application uses a database to store data, this is another source of potential vulnerabilities. SQL Injection is a very common attack that involves maliciously crafted user input designed to change the logic of a query. This potentially allows the user to run any kind of query or bypass security measures. Stopping it is usually as easy as properly escaping data, or using prepared statements.
Escape functions:
- mysql_real_escape_string(): For use with the mysql_* functions
- mysqli::escape_string(): For use with the MySQLi extension/class
- pg_escape_string(): For use with PostgreSQL
- addslashes(): This is a generic escape function to use only if your database engine does not have a specific function
Here is an example using each function:
// $db refers to database connection resource/object
$name = "O'reilly"; // Contains a quote that will break the query
// MySQL via mysql_*
pe_string($name, $db);
// MySQL via MySQLi
$safe = mysql_real_esc
a$safe = $db->escape_string($name); // OOP Style
Procedural Style
// PostgreSQL
$safe = pg_escape_string($db, $n
$safe = mysqli_real_escape_string($db, $name); /
/ame);
Generic (last resourt)
/
/$safe = addslashes($name);
4.Hide Your Errors
It's never a good idea to show the world your errors. Not only does it make you look bad, it also might give malicious users another clue to help them break your site. You should always have display_errors disabled in a production environment, but continue logging errors with log_errors for your own information.
These PHP configuration directives are suitable for a production server:
display_errors 0
log_errors 1
5.Use Post For Dangerous Actions
There are two common methods used to send data to a PHP application, GET and POST. GET works by adding variables to the end of URL's (eg. http://www.example.com/process.php?action=delete&id=123). POST works by sending variables in the body of the request (normal users will not see them). It is important to carefully consider which method to use for a certain task.
You should generally stick to POST when you are performing a potentially dangerous action (like deleting something). The reason is that is is much easier to trick a user into accessing a URL with GET parameters than it is to trick them into sending a POST request. Take this example:
If a user with an active session on your site visits another web page with the above image tag, the user's browser will quietly send a request to your site telling it to delete record 123.
Keep in mind that other precautions should also be taken to ensure requests are legitimate under a secure session. It is also easily possible to create a form that does the same as above using a POST request, so don't assume that method is "safe" either. See sections 2 and 4 of the PHP Security Guide for more information on form and session security.
Conclusion
This article is just a general overview of PHP security practices. For more detailed explanations if the topics covered here as well as some not covered, see the PHP Security Guide by the PHP Security Consortium. There are also many other articles and books on the topic that you may be interested in.
Of course there may be some that disagree with some of the details in this post. Don't hesitate to post a comment if you have any corrections, improvements, or additions!