Variables
- Always initialise variables, particularly if register_globals is enabled.
Ensure input is filtered
- All user-supplied data must be inspected to ensure that it is valid before it is accepted for use. This includes all $_GET, $_POST (including hidden fields!), cookie (and session?) variables.
- Use a 'whitelist' approach: Data is considered invalid unless it can be proven to be valid.
- Check all required variables are present.
- Check that values have been assigned.
- Check the data type is as expected. Functions include:
- is_string()
- is_numeric()
- is_float()
- is_array()
- is_object()
- Ctype functions permit more specific screening of input (see PHP manual).
- Check the value is within acceptable ranges. For example:
- A select box should only return one of the listed values.
- Names should only include alphanumeric characters.
- Reject data that does not conform to expectations.
Eliminating SQL injection
- An SQL injection vulnerability exists whenever you used un-escaped data in an SQL query.
- Use msql_real_escape_string() to escape output.
- $username = mysql_real_escape_string( $previously_filtered_data[ 'username' ] );
- All values within a query should be contained within quotes regardless of data type.
- Queries that alter the database should be within a $_POST request rather than $_GET.
Cross-site scripting (maybe should be called 'html injection')
- A vulnerability exists whenever un-escaped data is output, eg:
- echo $_POST[ 'user_input' ] // what if user submitted something nasty?
- Escape the data with htmlentities:
- $html[ 'user_input' ] = htmlentities( $_POST[ 'user_input', ENT_QUOTES, 'UTF-8');
- echo $html;
- URLs containing variables that could potentially have been manipulated by users need to be escaped twice:
- $URL[ 'value' ] = urlencode( $value ); // escapes the value variable
- $link = "http://www.link.com?variable={$url[ 'value' ]}";
- $html[ 'link' = htmlentities( $link, ENT_QUOTES, 'UTF-8' ); // escapes the html
Session fixation
- May be conducted as a prelude to session hijacking. To prevent, regenerate the session identifier with session_regenerate_id() whenever there is an escalation of privilege (eg. logging in).
Filenames
- Be wary of opening files with user-supplied 'filename data'. Users may pass something nasty instead, like a relative path to a sensitive local file or a URL to open a remote file with exploit code. Don't forget that fopen(), include() and require() all accept URLs as arguments. To prevent problems:
- Disable remote file access (allow_url_fopen).
- Use the open_basedir option in PHP to restrict filesystem access.
- Check filenames with realpath() and basename().
- If you have to allow users to specify a file name, use a combination of realpath() and basename() to verify the relative path to the file on your site:
- realpath() returns the full path to a file, resolving any special characters like directory traversals.
- Basename() just returns the file name from the path.
- Combine them to verify the file name by resolving the full path to the file then extracting the file name and comparing it with the user-supplied one:
File uploads
- Don't use user-supplied filenames sent by a browser. Create your own temporary file name to handle it.
- Set a maximum uploadable file size with post_max_size in php.ini to avoid DOS attempts to jam your server.
- Cookies can be used to overwrite global variables in GET and POST. Check that the uploaded file really *has* been uploaded using is_uploaded_file()
- Use PHP's move_uploaded_file() which moves a file only if it was uploaded. Use this in preference to copy() or system level functions.
File access
- Restrict filesystem acess to a specific directory using the open_basedir in php.ini.
- Store sensitive data in the database in preference to files [perhaps trust_path is a better option in Impress?].
- If you are on a shared server ask the hosting company to store your session files in your own session directory, because otherwise you are probably sharing a session directory with all the other people on the server and everyone's scripts can access everyone else's session files. This involves setting a session path in the VirtualHost block in Apache's httpd.conf.
- php_value session.save_path /myown/path
- If you have .htaccess rights and Apache is configured to let you over-ride options you may be able to make this change yourself.
PHP code
- Disable dangerous function calls like system() and eval() by setting disable_functions in php.ini.
- disable_functions system, eval
Environment
- Disable the following in php.ini:
- register_globals
- magic_quotes_gpc
- allow_url_fopen