Top 5 Secure Coding Tips for PHP applications

By balaji

December 17, 2009

In this article, we will be looking at the top 5 best practices to develop secure code in PHP. These include filtering of input data to eliminate unexpected input, securing database queries using parameterization, filtering of output data, error handling through custom errors and preventing other forms of injection attacks.


In this article, we will be looking at the top 5 best practices to develop secure code in PHP. These include filtering of input data to eliminate unexpected input, securing database queries using parameterization, filtering of output data, error handling through custom errors and preventing other forms of injection attacks.

Filter Input Data

A majority of security vulnerabilities can be addressed by filtering data from user input and other external sources. User inputs from URLs, forms and cookies are accessible through the superglobal arrays $_GET, $_POST and $_COOKIE. Before accepting the values as it is, validate/filter them to make sure they don't contain unexpected input.

PHP 5.2.0 and greater version comes with a very convenient set of data filtering functions. These functions allow validating common things such as emails and URLs, which would otherwise require complex regular expressions that don't always work. These functions are filter_var(), filter_input(), filter_id() etc.

This way you never have to touch the raw input via the $_GET or $_POST arrays.

Securing Database Queries

Use of prepared statements or parameterized queries is the secure option when it comes to interaction with the database. In most applications, the input entered by the user is used to dynamically construct the query that is sent to the database. The user input can be maliciously crafted to change the logic of a query. This potentially allows the user to run any kind of query or bypass security measures.

PHP supports MySQL prepared statements using the Mysqli (MySQL Improved) extension in PHP 5 via the MySQLi_STMT class.

A sample implementation of a prepared statement using mysqli extension is as follows:

/* Create a prepared statement */
$stmt = $mysqli -> prepare("SELECT priv FROM testUsers WHERE username=?
AND password=?")

Observe that placeholders (?) are being used for variables in the above statement. The same query using concatenated SQL statements appears as follows:

Stmt = "SELECT priv FROM testUsers WHERE username= $user AND
password = $pass";

Here the user inputs are directly used in the query through $user and $pass variables and hence can lead to SQL injection. But the use of place holders (?) for variables can mitigate this flaw.

/* Bind parameters: s - string, b -  boolean, i - int, etc */
$stmt -> bind_param("ss", $user, $pass); //Two "ss" is
//used for two string variables user and pass.
/* Execute it */
$stmt -> execute();
/* Bind results */
$stmt -> bind_results($result);
/* Fetch the value */
$stmt -> fetch();

If the application has to use concatenated SQL statements, the PHP comes with a set of functions that can help prevent SQL injection:

  • mysql_real_escape_string(): Escapes a string for use in SQL statements
  • mysql_escape_string(): Escapes the string (PHP versions before 4.3.0)
  • addslashes(): This is a generic escape function to use only if your database engine does not have a specific function
  • quoteSmart(): Formats input so it can be safely used as a literal

Filter Output Data

Escaping of special characters is very important in order to block certain attacks involving execution of malicious Javascripts by the browsers. PHP provides few built-in functions for cleaning up the text before displaying it to the users:

  • 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)
  • strtr(): Translate certain characters

Error Handling

The raw error messages returned from application, database, or external programs, should not be displayed to the users directly. The detailed information revealed in error messages might give a clue to malicious users to help them break the application completely. Error messages can be put in server's error log instead of displaying them to a user with these configuration directives:

  • log_errors = On
  • display_errors = Off

Preventing other injection attacks

The ability to execute an arbitrary file/command is a security risk and should be protected against. In PHP, the injection flaws include:

  • Remote File Inclusion
  • Local File Inclusion
  • Command Injection

File inclusion vulnerability is the dynamic execution of interpreted code loaded from a file. This file could be loaded remotely from an http/ftp server in the case of remote inclusions, or locally from disk.

Remote File Inclusion

RFI allows an attacker to include remote file on the web server. The PHP language has an allow_url_fopen directive and if enabled it allows filesystem functions to use a URL which allows them to retrieve data from remote servers. An attacker can alter a variable that is passed to one of these functions to cause it to include malicious code hosted on his remote webserver. An example of this:

include($_GET['content'] . '.php');

$_GET['content'] fetches the value of variable "content" from URL, and . '.php' appends the extension to it. Then the requested page is included, and its code is executed on the server

Local File Inclusion

Local File Inclusion is almost similar to RFI, the only difference is that an attacker can include files which are hosted on the local server. The following is an example of local file inclusion vulnerability in PHP:

require_once($LANG_PATH  . '/' . $_GET['lang'] . '.php');

In this case an attacker controls the "lang" variable and can thereby force the application to execute an arbitrary file as code.


Command Injection

Command injection attack is to inject and execute commands specified by the attacker in the vulnerable application. It occurs when the user input is passed to shell via some commands like exec(), system() etc.

The injection flaws can be prevented by following good practices such as:

  • Validate all kind of content passed by the user to an application; this includes parameters passed as part of a GET or POST request to the application
  • Limit the use of dynamic inputs from users to vulnerable functions either directly or via wrappers
  • Set strong permissions on all well known critical files on the webserver so that they cannot directly be accessed by an adversary
  • Ensure that all variables are properly initialized prior to first use or disable register_global, allow_url_fopen

Also few PHP functions are available to escape the input like escapeshellcmd(), escapeshellarg(), escapeshellcmd().


Tags: Best Practices