A short checklist outlining the most common vulnerabilities of web applications and applicable PHP solutions and best practices. Although focused on PHP, this checklist can be extended to all programming languages.
error_reporting = E_ALL(without removing the
E_NOTICE) is used to recognise uninitialised variables; in production, errors should not be displayed in the web interface;
filter_input ()statements are very useful for filtering and validating inputs;
===type avoids errors during comparison with Booleans. The expression
(0 == false)is true while
(0 === false)is false;
is_numericfunction to a (int) $value type conversion);
$_REQUESTglobal super variable because it may contain values from many different sources;
isset, for example
isset ($ _ GET ['id']);
$_FILES ['file'] ['name']through the
basenamefunction when uploading files; (imagine
$_FILES ['file'] ['name'] = '../../../etc/passwd')
getimagesize()function, which returns false when the file is not of this type. Think also about the fileinfo extension;
In general it is advisable to use the concept of a whitelist for many cases of validation.
An extra layer of protection or protection of an already existing application can be provided by adding “PHP Input Filter” and “PHPIDS” or “mod_security” if you use Apache
Using raw SQL queries by concatenating variables to the query string is bad practice that can easily lead to unwanted SQL injection.
mysql_real_escape_stringfunction. It is useful to add that the PHP manual advises against using this function, but it can be useful for quick securing of an old application.
add_slashes. Each database will react differently to these escape methods.
htmlentitiesfunction. Some “templating systems” such as “flexy” make an automatic escape;
In general, a generic display function must be written and used during any display.
Injection of malicious code that will be executed by the application.
requireinstructions. If it cannot be avoided, the use of whitelist filters is important;
offif the function is not required.
Some functions can be used to run system instructions.
escapeshellcmd()functions must be used;
basenamefunction in filenames.
By stealing the session identifier, other users’ sessions can be used. See “firesheep”, for example.
link?PHPSESSID=123). So set
Involves the use of the open session in another browser tab by a malicious web page. Most often the vulnerable parts will be forms, which could be completed by a malicious site. In this case it is easy to avoid this type of attack by creating a unique identifier for the form and checking it during form submission. It is good practice to use a nonce created by a function such as uniqid or pseudo random hash with a secret code by using the
hash_hmac function, which is placed in a hidden field on the form and in session, to be checked during submission.
To protect non-form functionalities, case-by-case analysis is required.
Passwords must be stored in the database in hashed form. To do this, it is important, above all, not to use md5 or sha1 type algorithms, for which there are “rainbow tables”, which can be used to obtain the password quickly from the hash.
In addition, specifically to avoid the use of rainbow tables, and to hide identical hashes from the same password, it is important to use a salt. This salt will be determined randomly and concatenated to the password before hashing. The salt and the hash will therefore be stored in the database.
It is also important to add a secret salt constant that would be stored somewhere other than in the database, for example in a configuration file.
The use of a concatenated salt can be usefully replaced by a
hash_hmac hash function, but we recommend the use of the
crypt function (http://php.net/manual/en/function.crypt.php) which enables a cost factor to be added to the calculation when using
CRYPT_SHA512 (see key stretching).
The hash calculation will then have the following form, for example:
$hash=crypt(hash_hmac('sha512','secret password',$saltsecretconfig), '$2y$’.$cost.'$randomsaltDB$') ;
The variable $cost will need to be set to a number such that the duration of the operation is viable for your application, but prohibitive for a brute-force attack (1/100 seconds for example).
PHP 5.5 introduces new hash functions that enable secure storage of passwords (see http://www.php.net/manual/en/function.password-hash.php).
The function above becomes:
$hash = password_hash(hash_hmac(‘sha512’, ‘secret password’, $saltsecretconfig), PASSWORD_BCRYPT, ['cost'=>$cost]); // from PHP 5.5
In general, a web application will be scanned with an automatic tool by an attacker before compromise. It is therefore possible to place cyber traps or other “tar pits” on the application. A practical example called “weblabyrinth” can be downloaded from http://www.mayhemiclabs.com/content/new-tool-weblabyrinth. But useless scripts containing “sleep” functions or other false authentications can already perform this function.
Placing libraries and other scripts in places that cannot be accessed directly from the Internet is a definite advantage. It means that libraries with vulnerabilities cannot be exploited directly.
It is not possible to defend entirely against denial of service type attacks. However, it is possible to optimise applications so that they respond better. Although this is beyond the scope of this document, the following are some concepts that can be explored:
Many frameworks are written by experienced developers and benefit from their extensive security experience and good programming practices. They often, therefore, include many of the techniques outlined above and enable web applications to be developed more quickly and securely. Examples: Zend, Symfony…
open_basedir configuration function is used to define the directories to which the application has access and therefore offers higher security.
The following php.ini parameters should be set to the needs of your application while trying to keep them as low as possible. In general it is advantageous to set these parameters on the fly with
ini_set in scripts that need more resources than the bulk of the application:
Check this value in the production environment:
display_errors = Off