PHP Security Best Practices-2

Author:

PHP security
PHP is a powerful and flexible tool. This power and flexibility comes from PHP being a very thin framework sitting on top of dozens of distinct 3rd-party libraries. Each of these libraries has their own unique input data characteristics. Data that may be safe to pass to one library may not be safe to pass to another.
A recent Web Worm known as NeverEverSanity exposed a mistake in the input validation in the popular phpBB message board application. Their highlighting code didn’t account for double-urlencoded input correctly. Without proper input validation of untrusted user data combined with any of the PHP calls that can execute code or write to the filesystem you create a potential security problem. Despite some confusion regarding the timing of some unrelated PHP security fixes and the NeverEverSanity worm, the worm didn’t actually have anything to do with a security problem in PHP.
When we talk about security in a web application we really have two classes. Remote and Local. Every remote exploit can be avoided with very careful input validation. If you are writing an application that asks for a user’s name and age, check and make sure you are only getting characters you would expect. Also make sure you are not getting too much data that might overflow your backend data storage or whatever manipulation functions you may be passing this data to. A variation of the remote exploit is the XSS or cross-site scripting problem where one user enters some javascript that the next user then views.
For Local exploits we mostly hear about open_basedir or safemode problems on shared virtual hosts. These two features are there as a convenience to system administrators and should in no way be thought of as a complete security framework. With all the 3rd-party libraries you can hook into PHP and all the creative ways you can trick these libraries into accessing files, it is impossible to guarantee security with these directives. The Oracle and Curl extensions both have ways to go through the library and read a local file, for example. Short of modifying these 3rd-party libraries, which would be difficult for the closed-source Oracle library, there really isn’t much PHP can do about this.
When you have PHP by itself with only a small set of extensions safemode and open_basedir are generally enough to frustrate the average bad guy, but for critical security situations you should be using OS-level security by running multiple web servers each as their own user id and ideally in separate jailed/chroot’ed filesystems. Better yet, use completely separate physical servers. If you share a server with someone you don’t trust you need to realize that you will never achieve airtight security.
Always Validate User Input!
All sorts of havoc are possible if a user passes unexpected content to a PHP script. To avoid that possibility, you must validate user input. This is perhaps the number one rule of any server side programmer.
Validate Client Side:
You can/should validate with javascript in any form which seeks user input. The advantage of javascript validation is that it will reject improper content without touching your server. However it provides no protection against intentional misuse.
Validate Server Side:
Even if you have strong client side validation, you must also validate on the server before performing any action with user provided input. If you have read any of the HTMLfixIT perl tutorials, you know how strongly we feel about using taints mode for this very reason. If you ask for one type of information, but receive another, a malicious user could hack your database, or gain access to critical system files.
Bake Without Cookies!
Cookies are sometimes used to pass important information, usernames, passwords, and login status. Much better that you learn about sessions. Sessions store the information server side and are much more difficult to hack, spoof or crack.
Avoid the Use of Variables in Includes and File Calls!
Some coders feel they are writing good open ended modular scripts when they say something like:

But consider the effect if someone passed this variable to the script:
http://www.yourdomain.com/script.php?page=http://remoteserver.com/nastyscript.php
It would load the remote server’s page, because PHP isn’t particular about what comes into it. So avoid using variables to include or require other pages.
Global Variables – and on-again, off-again proposition:
Global variables were thought to be a great feature of PHP. Imagine the effort that can be saved by simply creating variables automatically from post and get. Unfortunately if carelessly used, they can cause problems. It is usually best to turn them off unless you know exactly what you are doing.
If you do feel the need to leave them on, its a good idea to pre-declair variables with fixed values prior to using them. 
ie
  $somevar= 1; # this variable can now be safely used as you have removed
# the chance of it retaining a value because a ?somevar=some_nasty_value
# string was added to the script address.

so that if a somevar param is passed to the script, it will be overwritten by the value you have specified manually, thereby making safe what could otherwise be a risky bit of code.
Be Careful Out There …
If any of you remember the TV series, Hill Street Blues, the Captian always released his police officers with the phrase, “be careful out there,” because the world can be a dangerous place. However, just because it can be less than perfect, don’t be afraid to venture forth, just spend a few minutes being sure your code is as tight as it can be from a security perspective.
Protecting Customer Data
By default, all data that transmits to and from your web sites is sent in clear text. This means that potentially all data sent can be easily captured by others. While it is possible to “encrypt” (garble) your data to make it more difficult to understand by an outsider, it is not considered a safe practice to do so with sensitive data. If you request sensitive data from your customers, such as a credit card, you must do so over a secure connection with SSL.
From Hackers point of view
Secure Socket Layer (SSL)
Requesting a credit card without properly using SSL could render you liable. Only data sent over SSL is considered to be safe for retrieving credit cards. You and your customer can decide to hand the entire credit card handling process to a third party broker, such as Paypal, where your site does not handle the credit card at all. This is a great method for a small scale site and is cost effective.
If you decide to use SSL, contact your hosting company to see if it is a service they offer. Many hosting companies include SSL as an included part of their hosting packages. This will likely include your admin setting up a special folder for storage of all your PHP files related to handling SSL transactions. You must send the user to a form residing inside this folder BEFORE asking for any sensitive data. Potentially every file involved at that point will need to be inside that folder. Even the images involved will reside in that folder since any files retrieved from elsewhere will trigger a warning from the browser. This is why many SSL enabled pages are stripped of ornamentation. Sending data over SSL effects transmission speed, since significant data processing is occuring on the client and server for encryption and decryption, even of the images. 
Cross Site Scripting (XSS)
When we present a text box allowing a user to type in data we are opening ourselves up to several potential risks. One of these is called Cross Site Scripting (XSS). XSS most commonly occurs when we do not strip out script tags and other dangerous HTML or JavaScript when we request information from our users. If we present the data back to the users, what becomes visible is JavaScript that can popup a new window, redirect the user to their site, which may masquerade as yours, or even copy your cookie data first, then send it to their site. THIS is how cookies are dangerous!
SQL Injection Attacks
A SQL injection attack occurs when someone attempts to hack into, or damage your database by inserting or effecting SQL commands aimed at your database. The usual methods are either through a textbox on an existing form, the querystring or by creating a custom form designed to inject SQL into your form handler.
There are two main types of attacks. First-order attacks are when the attacker receives the desired result immediately, either by direct response from the application they are interacting with or some other response mechanism, such as email. Second-order attacks are when the attacker injects some data that will reside in the database, but the payload will not be immediately activated.
A hacker can use the “;” character in a text box, and therefore try to run more than one command at once. The first command could be the SQL command you intended. The second one could be to drop your customer table!
An even simpler attack can be used on a typical Password field by adding the string:
‘ OR ”=’
Which when added to a typical Login and Password SQL statement allows you to login as the first user in the login table!Once logged in, the hacker can try to determine the names of the fields and tables of your database, etc.
Developers point of view
Refusing Invalid Data
While it is nice to imagine you can attempt to “strain out” bad data as input by users, it is safer to simply refuse data from users who do not comply. This is especially true in the case of a login. We can use regular expressions in a function to determine if input consists of only alpha-numeric input with the following function:
function onlyAlphaNum($myString){
$myReturn = eregi(“[^a-zA-Z0-9]”,$myString);
if($myReturn){return false;}else{return true;}
}

The function above returns a simple true or false based on whether the user has truly entered only numbers and letters. You would call the function, and redirect the user with code like this:
$myReturn = onlyAlphaNum($Password);
if(!$myReturn){myRedirect(“login.php?msg=3”);die();} //redirect user indicating wrong login/password
This is a quick function to help us deny data from would be hackers. However, what if the user is inputting an email, as we request they do on our login page? That would not pass the test, with the “@”, symbol, etc. To check for an appropriate email, lets adapt our function and create a new one that only allows what is close to a valid email:
function onlyEmail($myString){
  if(eregi(“^[a-zA-Z0-9_-.]+@[a-zA-Z0-9_-]+.[a-zA-Z0-9_-]+$”,$myString))
  {return true;}else{return false;}
}
This version checks to make sure what is sent is similar to the “onlyAlphaNum” function, except that it will allow periods and an asterisk in the appropriate locations.
Hiding Error Messages, Data Structure
Once you study SQL Injection, you will see that the error messages shown by the database help a hacker determine how your database is structured. Remember to change the HIDE_ALL_ERRORS constant in your config_inc.php file to TRUEbefore you go public with your site.
To help hide the structure of your database, change the element names of your forms so they do NOT match your database!  Don’t make it TOO easy to guess your field names!
Add prefixing to your tables so obvious table names don’t exist!  The further benefit of adding a prefix is you limit the possibility of collisions with tables for other applications.
Limiting MySQL User Permissions
A simple, but HIGHLY effective way to protect the data in your database is to set up separate MySQL users for each need of your users & administrators. 
Here is an example.  What if a PHP page involved is open to the public, such as a textbox for searching?  Lets create a “select only” user specifically for searchers:
grant select on dbname.* to username identified by ‘horsey123’;
This limits the public (for this text box, anyway) to only selecting data, not adding, deleting, dropping or altering data.
We’ve built this capability into our connINC.php file. You can setup credentials passed to the conn() function as one of the following strings:
admin
delete
insert
update
select
Each level of access should include the capabilities of those below it. MySQL accounts must be setup for each level, with ‘select’ account only able to access db via ‘select’ command, and update able to ‘select’ and ‘update’ etc. Each credential set must exist in MySQL before it can be used.
Type and Length
The minimum checks on any variables coming from other sources is for type and length. These type of checks help to assure that the data that is being recieved is of a type and length that is expected. An example might be numeric value that is required. If the number needs to be between 1 and 10, and a user submits 999, then there is a possibility of causing an error or warning that is displayed in the browser. These warnings give information about the path of the script that caused the error and so, a malicious user, gains valuable information about the system for an attack.
PHP has many ways of checking user input with the Filter functions and possibly the most useful tool the strlen()function.
Error Reporting
PHP contains many levels of error reporting and it is a very useful addition in the developers tool kit. By reporting runtime errors, error reporting lets the developer know what problem has occured, the path name and file name of the script that has the error, the function name that has possibly caused the error and the line number on which the error occured. Should a malicious user succeed in causing an error on a site, all this information about the system is gained from the error output.
To remedy this the php.ini setting display_errors should be turned off. This ensures if any sort of error occurs, no output is generated and potentially giving miscreants a free ride. If this setting is not turned off in php.ini, it can be turned off on a per script basis at runtime with the ini_set() function as follows.


Of course, the errors should be recorded, so it is important that errors are logged so that the developer can debug any potential problems. If logging is not enable in php.ini, it can be activated with the ini_set function as follows.