10 steps for securing a PHP app
Tags: php, security
Published: 2019-10-14 14:49:21

---
title: 10 steps for securing a PHP app
published: true
description: Configuration options for a more secure PHP environment
tags: php, security
cover_image: https://thepracticaldev.s3.amazonaws.com/i/ref2975csus4af96hx9l.jpg
---

Hello PHP devs. In this post, I'll try and provide you with some concrete steps you can make to improve the security of your PHP app. I'm focusing on the PHP configuration itself, so we won't talk about SQL injections, HTTPS or other non PHP related issues.

I'll illustrate examples with bash lines from my `docker-entrypoint.sh` script, but of course you can apply it to a non docker environment.

## Sessions

### Use longer session id length

Increasing the session id length will make it harder for an attacker to guess it (via bruteforce or more likely side-channel attacks). Session ID length can be between 22 to 256 characters. The default is 32.

~~~bash
sed -i -e "s/session.sid_length = 26/session.sid_length = 42/" /etc/php7/php.ini
~~~

(don't ask me why it's 26 on Alpine Linux...)

You might also want to look at session.sid_bits_per_character.

### Use a custom session save path with restrictive permissions

Only nginx/php needs to access the sessions, so let's put them in a special folder with restrictive permissions.

~~~bash
sed -i -e "s:;session.save_path = \"/tmp\":session.save_path = \"/sessions\":" /etc/php7/php.ini
mkdir -p /sessions
chown nginx:nginx /sessions
chmod 700 /sessions
~~~

Of course, if you use Redis to handle sessions, you don't care about this part ;)

### Secure session cookies

[session.cookie_httponly](https://www.php.net/manual/en/session.configuration.php#ini.session.cookie-httponly) to prevent javascript from accessing them. [More info](https://blog.codinghorror.com/protecting-your-cookies-httponly/).

~~~bash
sed -i -e "s/session.cookie_httponly.*/session.cookie_httponly = true/" /etc/php7/php.ini
sed -i -e "s/;session.cookie_secure.*/session.cookie_secure = true/" /etc/php7/php.ini
~~~

[session.cookie_secure](https://www.php.net/manual/en/session.configuration.php#ini.session.cookie-secure) to prevent your cookie traveling on cleartext HTTP.

[session.cookie_samesite](https://www.php.net/manual/en/session.configuration.php#ini.session.cookie-samesite) to prevent Cross-Site attacks. Only for recent PHP/browsers.

### Use strict mode

Due to the cookie specification, attackers are capable to place non removable session ID cookies by locally setting a cookie database or JavaScript injections. [session.use_strict_mode](https://www.php.net/manual/en/session.configuration.php#ini.session.use-strict-mode) can prevent an attacker initialized session ID of being used.

### Restrict lifetime

Sessions should close with the browser. So set [session.cookie_lifetime](https://www.php.net/manual/en/session.configuration.php#ini.session.cookie-lifetime) to 0.


## Open_basedir

[open_basedir](https://www.php.net/manual/en/ini.core.php#ini.open-basedir) is a `php.ini` config option that allows you to restrict the files/directories that can be accessed by PHP.

~~~bash
sed -i -e "s#;open_basedir =#open_basedir = /elabftw/:/tmp/:/usr/bin/unzip#" /etc/php7/php.ini
~~~

Here I have added unzip as it is used by composer. `/elabftw` is where all the source php files are located. And I don't remember why `/tmp` is here but there is surely a reason.

## Disable functions

Be careful with this, as you can very easily mess up an app. But this is definitely something to look into. Let's say an attacker somehow managed to upload a webshell, with this correctly in place, the webshell won't really work as `shell_exec` and friends will be disabled. I'm providing a list that works for [elabftw](https://github.com/elabftw/elabftw) but it's not 100% complete.

~~~bash
sed -i -e "s/disable_functions =/disable_functions = php_uname, getmyuid, getmypid, passthru, leak, listen, diskfreespace, tmpfile, link, ignore_user_abort, shell_exec, dl, system, highlight_file, source, show_source, fpaththru, virtual, posix_ctermid, posix_getcwd, posix_getegid, posix_geteuid, posix_getgid, posix_getgrgid, posix_getgrnam, posix_getgroups, posix_getlogin, posix_getpgid, posix_getpgrp, posix_getpid, posix_getppid, posix_getpwnam, posix_getpwuid, posix_getrlimit, posix_getsid, posix_getuid, posix_isatty, posix_kill, posix_mkfifo, posix_setegid, posix_seteuid, posix_setgid, posix_setpgid, posix_setsid, posix_setuid, posix_times, posix_ttyname, posix_uname, phpinfo/" /etc/php7/php.ini
~~~

## Disable url_fopen

The option [allow_url_fopen](https://www.php.net/manual/en/filesystem.configuration.php#ini.allow-url-fopen) is dangerous. Disable it. More info [here](https://security.stackexchange.com/questions/103427/what-are-php-allow-url-fopen-security-risk).

~~~bash
sed -i -e "s/allow_url_fopen = On/allow_url_fopen = Off/" /etc/php7/php.ini
~~~

## Disable cgi.fix_pathinfo

You don't want to allow non PHP files to be executed as PHP files, right? Then disable this. [More info](https://nealpoole.com/blog/2011/04/setting-up-php-fastcgi-and-nginx-dont-trust-the-tutorials-check-your-configuration/).

~~~bash
sed -i -e "s/;cgi.fix_pathinfo=1/cgi.fix_pathinfo=0/g" /etc/php7/php.ini
~~~

## Hide PHP version

To finish, a nobrainer:

~~~bash
sed -i -e "s/expose_php = On/expose_php = Off/g" /etc/php7/php.ini
~~~

That's it for now. I hope you'll find this post useful and improve your configurations ;)

Let me know in the comments if I've missed something important!

~Nico