Security is getting more and more important, and the PHP community has been doing great improvements in this topic during the last few years. From better configuration settings to provide some level of security by default to frameworks providing functionality to avoid common attacks such as XSS, CSRF or SQL injection.

If you don't know that a bug exists yet, is it still a bug?

If you don’t know that a bug exists yet, is it still a bug?

There are dozens of measures we can take in order to prevent and minimize security issues, both in the server and in the project’s code:

  • Disable PHP modules that are not in use.
  • Restrict information leakage (don’t display PHP errors or extra information like PHP version)
  • Turn off remote execution (allow_url_fopen and allow_url_include should be Off)
  • Control the size of POST requests and uploaded files.
  • Control the maximum time and memory PHP can take to minimize DoS attacks.
  • Disable dangerous functions such as eval() or exec() if they are not going to be used.
  • Disable session IDs in URLs to prevent session fixation.
  • Include a token in your forms to avoid CSRF.
  • Properly escape the output to avoid XSS.

Recommended article: “Linux: 25 PHP Security Best Practices For Sys Admins”

So, our server and our code is ready. But, what about code that we don’t have full control of? Composer has dramatically changed the way we organize our projects, with many small packages and libraries, and this adds a new challenges for keeping our projects secure. The question is: is it safe to rely on code that others have written? are these package maintainers taking the same care as I do in my projects?

Well, any piece of software can have bugs, and obviously open source projects are not an exception. The good point is that security researchers, once they find a vulnerability, it is reported and added to a database of known vulnerabilities. We basically need to find a way to avoid using code with known vulnerabilities, and there are some interesting tools out there to help us.

Tools

The main goal of automatic tools is to let you know of security issues as soon as possible. Here, I present four tools to check PHP packages, configuration settings and PHP versions.

Security Advisories Checker

This service, created by SensioLabs (the company behind Symfony) checks your composer.lock file to find packages you depend on that have known security issues. These security issues are included in a database called Security Advisories Database, which is public and updated frequently.

There are three ways to use the service:

* Online checker: uploading the composer.lock file.
* CLI Checker: to check files from the command line.
* API: it can be integrated in your own projects.

For example, if we execute the CLI tool to check a project using Symfony 2.1 and an older version of Swiftmailer, we get a few vulnerabilities:

$ security-checker security:check composer.lock

Security Check Report
~~~~~~~~~~~~~~~~~~~~~

Checked file: /tmp/composer.lock

[CRITICAL]
2 packages have known vulnerabilities

swiftmailer/swiftmailer (v4.2.2)
--------------------------------

* Sendmail transport arbitrary shell execution
http://blog.swiftmailer.org/post/88660759928/security-fix-swiftmailer-5-2-1-released

symfony/symfony (2.1.x-dev)
---------------------------

* CVE-2014-4931: Code injection in the way Symfony implements translation caching in FrameworkBundle
http://symfony.com/blog/security-releases-cve-2014-4931-symfony-2-3-18-2-4-8-and-2-5-2-released

* CVE-2014-6072: CSRF vulnerability in the Web Profiler
http://symfony.com/blog/cve-2014-6072-csrf-vulnerability-in-the-web-profiler

* CVE-2014-6061: Security issue when parsing the Authorization header
http://symfony.com/blog/cve-2014-6061-security-issue-when-parsing-the-authorization-header

* CVE-2014-5244: Denial of service with a malicious HTTP Host header
http://symfony.com/blog/cve-2014-5244-denial-of-service-with-a-malicious-http-host-header

* CVE-2014-5245: Direct access of ESI URLs behind a trusted proxy
http://symfony.com/blog/cve-2014-5245-direct-access-of-esi-urls-behind-a-trusted-proxy

This checker can only detect vulnerabilities that are referenced
Disclaimer in the SensioLabs security advisories database. Execute this
command regularly to check the newly discovered vulnerabilities.
```

If you check your dependencies and get any vulnerability, please upgrade your packages as soon as possible.

Roave Security Advisories

The Roave team has created a Composer package called roave/security-advisories that prevents you from installing packages with known security issues by using an smart trick: it adds all packages with vulnerabilities to the `conflict` section, making them unavailable for your project.

    "conflict": {
        "contao/core": ">=2.11.0,<2.11.16|>=3.0.0,<3.1.0|>=3.1.0,<3.2.0|>=3.2.0,<3.2.7|>=2.11.0,<2.11.17|>=3.0.0,<3.1.0|>=3.1.0,<3.2.0|>=3.2.0,<3.2.9|>=2.11.0,<3.0.0|>=3.0.0,<3.1.0|>=3.1.0,<3.2.0|>=3.2.0,<3.2.19|>=3.3.0,<3.4.0|>=3.4.0,<3.4.4",
        "doctrine/dbal": ">=2.0.0,<2.0.8|>=2.1.0,<2.1.2",
        "doctrine/doctrine-module": "<=0.7.1|<=0.7.1",
        "doctrine/orm": ">=2.0.0,<2.0.3",
         ...
    }

So you only have to add this line to your `composer.json` file and say goodbye to those packages!

"require":{
    ...
    "roave/security-advisories": "dev-master"
}

Keep in mind that due to the nature of the problem, there are no releases or stable versions of the package, you need to rely on the dev-master branch, which is updated periodically. So the package is only suited for installation in the root of your deployable project.

To generate the list of packages, the same list as in the previous tool is used.

iniscan

iniscan is a tool created by Chris Cornutt that scans your php.ini file to check for possible security issues from your configuration settings. The list of tests that iniscan performs can be seen by executing “iniscan list-tests”. Currently, there are around 40 tests, some of them are listed below:

  • allow_url_fopen: (must be Off) It is recommended to not allow opening remote files.
  • allow_url_include: (must be Off) It is also highly recommended to not be able to include remote files.
  • display_errors: (must be Off) You should never display PHP errors in production.
  • expose_php: (must be Off) If is set to On, it adds a HTTP header with information about the PHP version. It is not recommended to expose additional information.
  • session.use_only_cookies: (must be On) It is not a good idea to allow passing session ids in URLs. If someone else intercepts the session id (it can also be predicted or guessed using brute-force) can use it to enter into the session.
  • session.cookie_httponly: (must be On) It makes cookies only accessible by the HTTP protocol, not by JavaScript. It helps to minimize the effects of a XSS attack.

After executing the command, you will get an output like this:

$ iniscan scan --fail-only
== Executing INI Scan [03.11.2015 08:11:40] ==

Status | Severity | PHP Version | Key | Description
----------------------------------------------------------------------
FAIL | WARNING | | session.cookie_domain | It is recommended that you set the default domain for cookies.
FAIL | ERROR | 5.2.0 | session.cookie_httponly | Setting session cookies to 'http only' makes them only readable by the browser
FAIL | WARNING | | session.hash_function | Weak hashing algorithms in use. Rather use one of these: sha224, sha256, sha384, sha512, ripemd128, ripemd160, ripemd256, ripemd320, whirlpool, tiger128,3, tiger160,3, tiger192,3, tiger128,4, tiger160,4, tiger192,4, snefru256, adler32, crc32, crc32b, fnv132, fnv164, joaat, haval128,3, haval160,3, haval192,3, haval224,3, haval256,3, haval128,4, haval160,4, haval192,4, haval224,4, haval256,4, haval128,5, haval160,5, haval192,5, haval224,5, haval256,5
FAIL | ERROR | 4.0.4 | session.cookie_secure | Cookie secure specifies whether cookies should only be sent over secure connections.
FAIL | WARNING | 5.5.2 | session.use_strict_mode | Strict mode prevents uninitialized session IDs in the built-in session handling.
FAIL | ERROR | 4.0.3 | allow_url_fopen | Do not allow the opening of remote file resources ('Off' recommended)
FAIL | WARNING | | display_errors | Don't show errors in production ('Off' recommended)
FAIL | WARNING | | expose_php | Showing the PHP signature exposes additional information
FAIL | WARNING | | post_max_size | Unless necessary, a maximum post size of 200M is too large
FAIL | WARNING | | display_startup_errors | Showing startup errors could provide extra information to potential attackers
FAIL | WARNING | | open_basedir | Restricting PHP's access to the file system to a certain directory prevents file-based attacks in unauthorized areas.
FAIL | WARNING | | upload_max_filesize | The max upload size should not be too high, to prevent server overload from large requests
FAIL | WARNING | | post_max_size | The max upload size should not be too high, to prevent server overload from large requests
FAIL | WARNING | | memory_limit | The standard memory limit should not be too high, if you need more memory for a single script you can adjust that during runtime using ini_set()
FAIL | WARNING | | xdebug.default_enable | Xdebug should be disabled in production
FAIL | WARNING | | disable_functions | Methods still enabled - exec, passthru, shell_exec, system, proc_open, popen, curl_exec, curl_multi_exec

23 passing
3 failure(s) and 13 warnings

pcc is a similar tool which also checks for PHP configuration settings.

versionscan

Unlike previous tools, versionscan does not check your dependencies or php.ini settings, it checks PHP itself to detect if known vulnerabilities are fixed or not. It can be really useful to execute in production environments. It was also created by Chris Cornutt.

It contains a local database of checks defined in a JSON file, so you will need to update the tool frequently to get updated lists of checks.

For example, if we execute the tool while using PHP 5.5.19 we get the following warnings:

$ versionscan scan
Executing against version: 5.5.19
+--------+---------------+------+------------------------------------------------------------------------------------------------------+
| Status | CVE ID | Risk | Summary |
+--------+---------------+------+------------------------------------------------------------------------------------------------------+
| FAIL | CVE-2015-0273 | 0 | Use after free vulnerability in unserialize() with DateTimeZone |
| FAIL | CVE-2014-9425 | 7.5 | Double free vulnerability in the zend_ts_hash_graceful_destroy function in zend_ts_hash.c in the ... |
| FAIL | CVE-2015-0232 | 6.8 | The exif_process_unicode function in ext/exif/exif.c in PHP before 5.4.37, 5.5.x before 5.5.21, a... |
| FAIL | CVE-2014-9427 | 6.4 | sapi/cgi/cgi_main.c in the CGI component in PHP through 5.4.36, 5.5.x through 5.5.20, and 5.6.x t... |
| FAIL | CVE-2014-9426 | 7.5 | The apprentice_load function in libmagic/apprentice.c in the Fileinfo component in PHP through 5.... |
| FAIL | CVE-2015-0231 | 7.5 | Use-after-free vulnerability in the process_nested_data function in ext/standard/var_unserializer... |
| FAIL | CVE-2014-8142 | 6.4 | Use-after-free vulnerability in the process_nested_data function in ext/standard/var_unserializer... |
+--------+---------------+------+------------------------------------------------------------------------------------------------------+

Scan complete
--------------------
Total checks: 315

Each line contains the CVE (Common Vulnerabilities and Exposures) ID associated to the vulnerability, the risk (a number between 0 and 10) and a quick summary of the issue. If you need more information about specific CVEs, you can look for them in the National Vulnerability Database.

By default, the tool checks vulnerabilities for the version contained in the `PHP_VERSION` constant, but it can also check other versions by using the “–php-version” argument:

$ versionscan scan --php-version=5.6.0
Executing against version: 5.6.0
+--------+---------------+------+------------------------------------------------------------------------------------------------------+
| Status | CVE ID | Risk | Summary |
+--------+---------------+------+------------------------------------------------------------------------------------------------------+
| FAIL | CVE-2014-9427 | 6.4 | sapi/cgi/cgi_main.c in the CGI component in PHP through 5.4.36, 5.5.x through 5.5.20, and 5.6.x t... |
| FAIL | CVE-2015-0232 | 6.8 | The exif_process_unicode function in ext/exif/exif.c in PHP before 5.4.37, 5.5.x before 5.5.21, a... |
| FAIL | CVE-2015-0273 | 0 | Use after free vulnerability in unserialize() with DateTimeZone |
| ...
+--------+---------------+------+------------------------------------------------------------------------------------------------------+

Scan complete
--------------------
Total checks: 315
Failures: 11

Conclusion

We have seen for different tools to help you to minimize security risks in your PHP projects. They are meant to detect known vulnerabilities as soon as possible, so you can take the appropriate measures.

Photo: “Just a boring traffic light”, by Tilemahos Efthimiadis