Best Practices for writing secure PHP scripts

Shubhdeep Rajput
Published in
9 min readOct 19, 2021

--

If you are in web application development domain then you must have at least heard about PHP. It is most widely used scripting language out there. Since its popular , many can write code using PHP but what matters is writing secure code. So, it is always good to follow some best practices to write secure scripts using PHP. You can get many guides for writing secure PHP scripts but here I am sharing some tips which I think will be helpful to beginners. I will be adding link to official documentation wherever possible , so that you can learn more about the functions I am going to talk about. It is advised to read documentation. So, with that said, lets start..

1.) Always Sanitize User Input

The fundamental security problem with applications is — All user input is unsecure. That’s why sanitizing user input is the most important and fundamental part for security of any application. So, always treat user input as an untrusted source.

PHP provides following functions for sanitizing user input:

  1. htmlspecialchars() — This function converts some predefined characters, which have special meaning in html, to their html entities which preserves their original meaning. You are recommended to read more about it from here.
  2. htmlentities() — This function converts all characters, that are applicable, to html entities. It should be used carefully as this may lead to excessive encoding and cause some content to display incorrectly if character sets change.More about it at https://www.php.net/manual/en/function.htmlentities.php .
  3. strip_tags() — As name implies, this function strips html, xml and php tags from string, making string safe to process on.Link for more info https://www.php.net/manual/en/function.strip-tags.php .
  4. addslashes() — This function returns a string with backslashes in front of predefined characters. This can help, for example, to prevent an attacker from terminating the variable assignment and appending executable code.
  5. real_escape_string() — The real_escape_string() / mysqli_real_escape_string() function escapes special characters in a string for use in an SQL query, taking into account the current character set of the connection.This function is used to create a safe SQL string that can be used in an SQL statement.

Above are some of the most used PHP functions for sanitization, which when used rightly, should make user input safe to process.

But the recommended way to sanitize user input is using PHP FIlters and filter_var() function.

You can read about this function at https://www.php.net/manual/en/function.filter-var.php and about the sanitization filters available at https://www.php.net/manual/en/filter.filters.sanitize.php .

2.) Use Prepared statements

While above sanitizations can be used to sanitize user input, the most used and recommended method to sanitize SQL statements is using Prepared statements or Parameterized Statements.

Prepared statements provide higher efficiency in executing SQL statements than executing them directly and protect from SQL injection attacks by escaping quotes and other special characters which require prepared statements set methods.

The prepared statement execution consists of two stages: prepare and execute. At the prepare stage a statement template is sent to the database server. The server performs a syntax check and initializes server internal resources for later use.

3.) Always validate user input

Always assume that users don’t know anything and you have to educate them. It’s your responsibility to educate them to provide only desired input. So, validating the user input is an important part of application security and ensures that input your application is getting is valid input.

Validation using Regex — Using regex effectively can be a great defence against invalid inputs. It provides you a freedom to decide how the input should be.

Validation using PHP filters — The recommended way. PHP provides many filters that can be used with the filter_var() function to validate user input. Read about them at https://www.php.net/manual/en/filter.filters.validate.php .

4.) Limit Directory Access

Limiting access of PHP to the root of the application will limit PHP from accessing files outside the application root directory tree.

The open_basedir configuration directive in php.ini will limit the files that can be opened by PHP to the specified directory-tree.

When a script tries to open a file with, for example, fopen() or gzopen(), the location of the file is checked.

When the file is outside the specified directory-tree, PHP will refuse to open it.

Open_basedir is a good protection against remote file inclusion vulnerabilities. For a remote attacker it is not possible to break out of the open_basedir restrictions if he is only able to inject the name of a file to be included. Therefore the number of files he will be able to include with such a local file include vulnerability is limited.

Other than that, in case an attacker tries to access /etc/passwd or any other sensitive file using directory traversal attack, open_basedir will prevent this.

5.) Upload Files Securely

If your application has nothing to do with file uploads then best practice is to turn file uploads off by setting file_uploads=Off. This will prevent file uploads even if the attacker somehow manages to upload files.

If your users do need to upload file then set the upload size limit using upload_max_filesize to control max size of file that can be uploaded.

Now when uploading files, always upload files using POST method and enctype=”multipart/form-data” in <form> tag.

Always check that the file being uploaded is of accepted type or else an attacker can upload a malicious code file.

The basic process that can be followed while uploading file is:

  1. Check if there are any errors in the upload.
  2. Check if the file type is allowed
  3. Check that the file is under the set file size limit
  4. Check if the filename is valid (if the filename has a /, it will affect the destination path).
  5. Check that the file doesn’t already exist at the target location (based on the name).
  6. Finally, upload the file.

6.) Control Your Resources(DOS Control)

You can prevent your resources(memory and bandwidth)from getting exhausted using a DOS attack.

You can set the maximum execution time of each php script, in some seconds using max_execution_time configuration directive in php.ini.

Another recommended option is to set the maximum amount of time each script may spend parsing request data which can be set using max_input_time configuration directive in php.ini.

Maximum amount of memory a script can consume can be set using memory_limit configuration directive in php.ini.

7.) Log and hide all errors in production application

Once you have deployed an application on a live server, first thing you should do is disable the display of errors since those errors can help hackers to escalate attacks on applications by providing them with valuable information. To do so, set display_errors=off in php.ini and after that, to log errors, set log_errors=on and error_log=var/log/httpd/your_file_name.php.

8.) Never ever use user input with sensitive functions

This can be a severe security loophole as you should always think that an attacker can always find a way to circumvent your defences and compromise at this level can be severe. So, it’s better that you should never use user input with sensitive functions such as eval(), require(), include(), etc.

Wherever possible, try to find other ways to implement functionality where user input is required or give users a whitelist of choices to choose from.

9.) Use headers carefully in APIs

Internal APIs should always have Access-Control-Allow-Origin, Access-Control-Allow-Methods, Access-Control-Allow-Headers, Access-Control-Allow-Credentials appropriately set as per their requirement. Read more about them at https://developer.mozilla.org/ .

Access-Control-Allow-Origin should never have value set to * . Either use fix value or use from list of allowed values.

10.) Turn off Remote Code Execution

The allow_url_fopen or allow_url_include option allows PHP’s file functions such as file_get_contents(), include(), require() to retrieve data from remote locations using ftp or http protocols. Improper input filtering can open them up to code injection vulnerabilities which are most severe vulnerabilities. If not absolutely necessary, set allow_url_fopen=off and allow_url_include=off in php.ini.

11.) Prevent PHP version leakage

This could either not impact your application or can have severe consequences. It depends on the version of PHP you are using. In any case, you should hide your PHP version as it could assist the attacker in furthering his attacks.

This can be done by setting expose_php = off in the php.ini file. It will prevent the web server from sending the X_POWERED_BY header to the client.

Above all are some configurations and use of some important functions in PHP that can help you a lot for securing your web applications. Now, you might be wondering that I haven’t written anything about those popular threats that you might have heard about. Well, don’t worry, until now I have written about basic things that are absolutely basic practices that you should follow. Now I will tell you about how above tips actually help you defend against those famous attacks.

Cross Site Scripting (XSS)

Cross site scripting is a type of attack in which an attacker can execute malicious script in vulnerable website. You can read more about this vulnerability from here.

Suppose you are having a search functionality in your application. Here is how you would code it:

$query = $_GET['query'];
echo 'Your Search: '.$query;

where $_GET[‘query’] is a get param is URL.

Now attacker can type in some javascript in place of get param and since there is no sanitization, browser will render that javascript and it will get executed.

What you can do to avoid this is, you can use htmlspecialchars() function like shown below:

$query = htmlspecialchars($_GET['query']);
echo 'Your Search: '.$query;

Using PHP filters can be other way to avoid such attacks.

SQL Injection Attacks

Supplying user input directly to sql queries can be very dangerous since sql queries directly interact with the databases. In SQL injection attacks, attacker supplies malicious input which, if application processes in an improper way, can alter the database and can also reveal contents of database to attacker. You can learn more about this attack from here.

Let me show you a scenario.

Suppose you have a query at back end which takes product id as an input, which is supplied in get param and shows details of that product if that product is released. This can be done with below query like this:

$pid = $_GET['pid'];
$query = "select * from products where pid = '".$pid."'";

Now suppose that you have some products in the database which are not yet released. What attacker can do is change id in get param to pid=4 or 1=1. Now, this will result in the query below:

$query = "select * from products where pid = 4 or 1=1";

Since, 1=1 will always be true, this can leak sensitive information.

You can avoid this using Prepared statements whch are explained above.

Denial of Service(DOS) attacks

As the name suggests, these attacks prevent actual users from accessing your application’s resources. These types of attacks can be prevented by simply changing some configurations as explained above.

So, as you can see that by following above tips you can prevent some of the most popular security threats. Hope these tips help you build more secure applications. Please do comment if you know some of such tips and give a clap if you find this story useful. I’ll be back soon with other such story. Until then, code securely and have a good day!!

--

--