Updated: 1 Dec. 2020
PHP remains the most popular server-side programming language: it is used by almost 80% of websites (source). This language continues to be developed, and PHP 8 was released last week ! This version brings new features and should enhance security.
However, the security of PHP builds up from its ‘historical’ core features. The following article does not replace a good knowledge of PHP, and there will be nothing to copy/paste directly into your files. But we believe that these tips and best practices will bring you long-term benefits if you understand and implement the different points according to your needs and context.
Today we cover PHP configuration, updates, code organisation and data filtering/escaping.
Configuration and Updates
Everything starts with a good knowledge of what you have in your webserver.
Ask yourself a few questions:
- What version of PHP am I running?
- What libraries am I using?
- What code am I including from the outside? Can I trust it?
- What framework am I running and what is its version?
- Is my operating system up-to-date?
If you cannot for sure answer these questions, then you’re missing something.
Having a centralized and up-to-date list of what you are using on your webserver/website is key for your security.
Then, from this list of components, you must be able to answer two questions:
- Do I really need this specific component? If not, remove it. An unused item is an unnecessary risk.
- How do I know when there is an available update for that component? (mailing lists, twitter, RSS feeds…)
Security configuration is an ongoing process that you must perform regularly. This rule always sounds obvious when starting a new project, but is quite rarely applied months after the website is live.
Before we move forward into the different security tips, you must remember on critical proverb: less is more.
Complex security = bad security. Keep it simple, and effective. Also remember that 100% secure does not exist.
Configuring PHP itself is a key step. Having an up-to-date environment is not enough.
Error reporting features are great to understand what’s wrong with your code, but keep it visible to end-users on development/staging environments only!
Your php.ini file allows you to configure three important items:
On a development/staging environment, you can both display and log errors, but you do not want to display them on a production environment. Displaying errors on the production is not very nice, and attackers are also loving them (it gives precious information about your technical architecture).
Logging errors can be interesting on production, but choosing which of them you want to be written in log files is critical, especially on high-traffic website (can lead to huge log files).
This is where the “error_reporting” parameter can help you, by specifying which type of errors you want. Values can be E_ALL (or E_ALL | E_STRCT depending on your PHP version), or for instance E_ALL ^(E_STRICT | E_DEPRECATED | E_NOTICE).
Read more about that parameter: http://php.net/manual/en/errorfunc.configuration.php#ini.error-reporting
Other important php.ini directives should be reviewed. You can go through the following list and review their specificities with the page dedicated to php.ini directives.
Organizing your Code Properly
Keeping your private code, classes and third party libraries outside of the webroot is a good practice. If for any reason an attacker can retrieve raw files from your webroot, your code will be a little bit more safe (and avoid reverse-engineering).
On the contrary, if classes and libraries are kept in the webroot, for instance www/my-classes/libX1.031/ then attackers might be able to detect which libraries your are using and maybe the versions.
“Filter on Input, Escape on Output”
One principle of security is “Never trust users”. Your legitimate users will probably not try to hack your website, but attackers do. Making the difference between legitimate and evil users is quite difficult, so you have to be cautious with what you receive from them.
Filtering inputs allows you to accept or reject data provided by users. That data can come from url parameters (GET), form values (POST), cookies, http headers.
A good strategy in terms of data validation is to define what you expect from users. It can be a format, or a set of values. Keeping an updated list of data inputs will ensure you keep track of all items you must filter.
From a technical standpoint, filtering can be achieved by using regular expressions (if you are good enough in writing rules) or by using external filtering libraries like “Respect/Validation”.
https://github.com/Respect/Validation (regularly maintained – a few days ago at the time of writing this article).
Filtering data does not mean you are finished with it!
Every bit or user-submitted data, whether directly outputted to the page or firstly stored in a database and then outputted, must be escaped.
PHP native functions exist to perform this type of actions:
htmlspecialchars() – http://www.php.net/manual/en/function.htmlspecialchars.php
htmlentities() – http://php.net/manual/en/function.htmlentities.php
strip_tags() – http://php.net/manual/en/function.strip-tags.php
urlencode() – http://php.net/manual/en/function.urlencode.php
json_encode() – http://php.net/manual/en/function.json-encode.php
mysqli_real_escape_string() – http://php.net/manual/en/mysqli.real-escape-string.php
addslashes() – http://php.net/manual/en/function.addslashes.php
A different option is to use the filter_var function with a given sanitize filter:
The next article, PHP Security Tips and Tricks #2, covers protections against most common attacks (injections, XSS, session hijacking, parameter tampering).