Security is very important for any application, particularly for web application. So the exam requires you have at least a very well understanding how the security problems actually affect todays web applications innovation. You must know some common security issues, the security issues not just regarding PHP itself. Since PHP is an embedded language, you should know the security defense on all three level, that is Web Server level, Data base level and PHP itself. In the rest of this post, list out a summary on this vast topic on Security. Be sure to check other sections(such as topics on Web Programming, Database, PHP Command Line Interface, etc) that related to security considerations.
Defense in Depth
.When you plan with defense in depth, you plan for failure. Rather than internal functions assuming the data they receive is already validated or escaped, they will check and confirm.
.Applications that demonstrate defense in depth are not only more resistant to attack when they are developed, they also remain more resistant over time as new attacks are developed, and as more code is added to them.
Principle of Least Privilege
.Your PHP applications by default has a lot of power, they execute as the web user (often apache or www-data) with all the rights and privileges thereof.
.If an attacker gains control of PHP through an application vulnerability this can be used and abused.
Terms: Validation vs Escaping
.These terms are often confused, or used interchangeably
.Validation or Filtering is the process by which you subject data to a series of rules, either it passes (validates) or does not.
.Escaping is the process by which you prepare data for a specific resource by “escaping” certain portions of the data to avoid confusion of instruction and data.
Whenever you receive data one of three things can be said about it:
.It is valid, the user entered the data you want, in the format you desire.
.It is invalid because the user either did not comply or did not understand the rules on the data you requested (eg. Poorly formatted phone number).
.It is invalid because the user is attempting to compromise your system.
Data from the end user can not be trusted, it must be validated before it can be used.
-How does user data get into your PHP application?
.Validate data first don’t save it for last.
.Fail early, tell the user what went wrong.
.All Input Is Tainted
If the data originates from a foreign source such as user form input, the query string, or even an RSS feed, it cannot be trusted. It is tainted data.
As a general rule of thumb, the data in all of PHP’s superglobals arrays should be considered tainted. The one exception to this rule is the $_SESSION superglobal array, which is persisted on the server and never over the Internet.
.Whitelist vs. Blacklist Filtering
Blacklists must be modified continually, and expanded as new attack vectors become apparent.
For this reason, whitelists afford stronger protection against attacks than blacklists.
.There are two major approaches to data validation, the whitelist and blacklist approach.
.Under the whitelist approach you select a series of valid characteristics (frequently characters) only data that follows these rules is accepted as valid.
.Under the blacklist approach you select a series of invalid characteristics, any data that contains these characteristics is considered invalid.
.Which do you prefer?
Functions to examine
. ctype_alnum — Check for alphanumeric character(s)
. ctype_alpha — Check for alphabetic character(s)
. ctype_cntrl — Check for control character(s)
. ctype_digit — Check for numeric character(s)
. ctype_graph — Check for any printable character(s) except space
. ctype_lower — Check for lowercase character(s)
. ctype_print — Check for printable character(s)
. ctype_punct — Check for any printable character which is not whitespace or an
. ctype_space — Check for whitespace character(s)
. ctype_upper — Check for uppercase character(s)
. ctype_xdigit — Check for character(s) representing a hexadecimal digit
. preg_match – Apply regular expression rules to data
. is_array – Check to see if variable is an array
$clean = array();
$clean[’username’] = $_POST[’username’];
$clean[’password’] = $_POST[’password’];
$colours = array(’Red’, ’Blue’, ’Yellow’, ’Green’);
if (in_array($_POST[’colour’], $colours))
$clean[’colour’] = $_POST[’colour’];
You should force the user to provide correct information rather than trying to clean and sanitize it on your own. If you attempt to sanitize the data, you may end up with bad data, and you’ll run into the same problems that result with the use of blacklists.
Output is anything that leaves your application, bound for a client. The client, in this case, is anything from a Web browser to a database server.
Whereas filtering input protects your application from bad or harmful data, escaping output protects the client and user from potentially damaging commands.
When you send data to a resource (database, web browser, etc.) that resource likely interprets certain characters as having a specific meaning, by escaping the output you tell the resource to ignore the meaning for that character.
.Filtering vs Escaping
Filtering ensures the validity of data coming into the application; escaping protects you and your users from potentially harmful attacks. Output must be escaped because clients—Web browsers, database servers, and so on—often take action when encountering special characters. For Web browsers, these special characters form HTML tags; for database servers, they may include quotation marks and SQL keywords. Therefore, it is necessary to know the intended destination of output and to escape accordingly.
Escaping output intended for a database will not suffice when sending that same output to a Web browser—data must be escaped according to its destination.
Escaping Output – Web Page
.strip_tags() – Remove anything that looks like an HTML tag from a string.
.htmlentities() – Convert any character that has a specific HTML entity into it.
.Htmlspecialchars() – Convert &, “, ‘, <, > into their entities
htmlspecialchars() and htmlentities() the latter being the most exhaustive and, therefore, recommended function for escaping.
Escaping Output – SQL
You have two choices, escape your data or use prepared statements.
To escape your data use a database specific function, the newest one available.
Prepared statements are preferred as they offer perfect separation.
Escape output intended for a database server, such as in an SQL statement, with the database-driver-specific *_escape_string() function; when possible, use prepared statements. Since PHP 5.1 includes PHP Data Objects (PDO), you may use prepared statements for all database engines for which there is a PDO driver. If the database engine does not natively support prepared statements, then PDO emulates this feature transparently for you. The database engine (or PDO, if emulating prepared statements) performs the hard work of actually escaping the values for use in the statement.
// First, filter the input
$clean = array();
$clean[’username’] = $_POST[’username’];
// Set a named placeholder in the SQL statement for username
$sql = ’SELECT * FROM users WHERE username = :username’;
// Assume the database handler exists; prepare the statement
$stmt = $dbh->prepare($sql);
// Bind a value to the parameter
// Execute and fetch results
$results = $stmt->fetchAll();
When set to On, the register_globals configuration directive automatically injects variables into scripts. That is, all variables from the query string, posted forms, session store, cookies, and so on are available in what appear to be locally-named variables. Thus, if variables are not initialized before use, it is possible for a malicious user to set script variables and compromise an application.
Note that a by-product of having register_globals turned on is that it is impossible to determine the origin of input.A best practice for maintainable and manageable code is to use the appropriate superglobal array for the location from which you expect the data to originate.
.With register globals enabled PHP automatically creates global variables for all variables received.
.Register globals was great, when you were learning, and no one else saw your applications.
.Register globals on it’s own is not a security risk, register globals combined with sloppy coding is the problem.
.To write secure code that remains secure in an environment with register globals enabled you must pre-initilize all your variables.
.Code with E_STRICT enabled, it will warn you of variables that are used before initialization.
Reproduce a form at another location and submit it by modifying the action to use an absolute URL.
.Cross-Site Scripting XSS
.Under a cross site scripting attack an attacker injects code into your page (forum post, shout box, etc) that contains code that re-writes the page to do something nefarious.
.Cross-Site Request Forgeries
.Under a cross site request forgery attack a site exploits another sites persistent user trust relationship to make something happen.
.<img src=http://www.amazon.com/buy/my/book height=“1” width=“1”>
.iFrames are another common tool leveraged in this technique.
A cross-site request forgery (CSRF) is an attack that attempts to cause a victim to unknowingly send arbitrary HTTP requests, usually to URLs requiring privileged access and using the existing session of the victim to determine access.
While proper escaping of output will prevent your application from being used as the vehicle for a CSRF attack, it will not prevent your application from receiving forged requests. Thus, your application needs the ability to determine whether the request was intentional and legitimate or possibly forged and malicious.
The token method involves the use of a randomly generated token that is stored in the user’s session when the user accesses the form page and is also placed in a hidden field on the form. The processing script checks the token value from the posted form against the value in the user’s session. If it matches, then the request is valid. If not, then it is suspect and the script should not process the input and, instead, should display an error to the user.
.Magic Quotes automatically escapes quote characters, backslash and null to make it safer to send data to databases.
.This isn’t really a good thing, data goes places other than databases, database accept meta characters other than the ones magic quotes deals with
.Gone completely in PHP6
.Disabled by default in PHP5
.A pain in the neck, you end up with code like this:
$string = stripslashes($string);
Sessions provide safer state, it may not nesesarily be safe.
Basically, sessions combine cookies containing a session ID with a local(ish) data store corresponding to that session id.
If the session id is compromised, or the data store is not secure (/tmp on a shared machine) sessions are still vulnerable to attack.
This attack has two sides, either an attacker tricks another user into clicking on a link providing a session id, or an innocent user pastes a link containing their session id to someone they shouldn’t have.
To defend against this type of attack don’t allow session IDs to come in over GET, and regenerate session ids when a user authenticates themselves.
It is possible, however, to set the session identifier manually through the query string, forcing the use of a particular session. This simple attack is called session fixation because the attacker fixes the session.
If the user logs in while using the provided session identifier, the attacker may be able to “ride” on the same session and gain access to the user’s account. This is why session fixation is sometimes referred to as “session riding.” Since the purpose of the attack is to gain a higher level of privilege, the points at which the attack should be blocked are clear: every time a user’s access level changes, it is necessary to regenerate the session identifier. PHP makes this a simple task with session_regenerate_id().
If the user login is successful, regenerate the session ID.
Session IDs are very random.
Predicting them is hard, it’s much easier to:
– Check out /tmp on a shared server, see what people have
– Intercept communications
– Implement session fixation
To defend, implement some browser fingerprinting
One way to identify the user in addition to the session identifier is to check various request headers sent by the client. One request header that is particularly helpful and does not change between requests is the User-Agent header. Since it is unlikely (at least in most legitimate cases) that a user will change from one browser to another while using the same session, this header can be used to determine a possible session hijacking attempt.
After a successful login attempt, store the User-Agent into the session:
$_SESSION[’user_agent’] = $_SERVER[’HTTP_USER_AGENT’];
Then, on subsequent page loads, check to ensure that the User-Agent has not changed. If it has changed, then that is cause for concern, and the user should log in again.
if ($_SESSION[’user_agent’] != $_SERVER[’HTTP_USER_AGENT’])
// Force user to log in again
Whereas most of the other attacks can be prevented by filtering input and escaping output, session attacks cannot. Instead, it is necessary to plan for them and identify potential problemareas of your application.
When a user first encounters a page in your application that calls session_start(), a session is created for the user. PHP generates a random session identifier to identify the user, and then it sends a Set-Cookie header to the client. By default, the name of this cookie is PHPSESSID, but it is possible to change the cookie name in php.ini or by using the session_name() function.
.open_basedir – Restricts PHP’s file acces to one or more specified directores – Relatively quick and easy
.safe_mode – limits file access based on uid/ gid of running script and file to be accessed – Slower, doesn’t work well with uploaded files
.Remote Code Injection
A remote code injection attack occurs when an attacker is able to cause your application to execute PHP code of their choosing. This can have devastating consequences
for both your application and system. When including files with include and require, pay careful attention when using possibly tainted data to create a dynamic include based on client input.
While this attack is very powerful, effectively granting the attacker all the same privileges enjoyed by the Web server, it is easy to protect against it by filtering all input and never using tainted data in an include or require statement.
The allow_url_fopen directive in PHP provides the feature by which PHP can access URLs, treating them like regular files—thus making an attack such as the one described here possible. By default, allow_url_fopen is set to On; however, it is possible to disable it in php.ini, setting it to Off, which will prevent your applications from including or opening remote URL as files (as well as effectively disallowing many of the cool stream features described in the Files and Streams).
While PHP provides great power with the exec(), system() and passthru() functions, aswell as the ‘ (backtick) operator, thesemust not be used lightly, and it is important to take great care to ensure that attackers cannot inject and execute arbitrary system commands.
escapeshellcmd() and escapeshellarg()
When possible, avoid the use of shell commands. If they are necessary, avoid the use of client input to construct dynamic shell commands.
There are three php.ini directives that remain important in a shared hosting environment: open_basedir, disable_functions, and disable_classes.
You may set the open_basedir directive in php.ini or on a per-virtual-host basis in httpd.conf.
The disable_functions and disable_classes directives work similarly, allowing you to disable certain native PHP functions and classes for security reasons. Any functions or classes listed in these directives will not be available to PHP applications running on the system. You may only set these in php.ini.