10 steps for securing a PHP app

eLabFTW - Oct 14 '19 - - Dev Community

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.

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.

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 to prevent javascript from accessing them. More info.

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 to prevent your cookie traveling on cleartext HTTP.

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 can prevent an attacker initialized session ID of being used.

Restrict lifetime

Sessions should close with the browser. So set session.cookie_lifetime to 0.

Open_basedir

open_basedir is a php.ini config option that allows you to restrict the files/directories that can be accessed by PHP.

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 but it's not 100% complete.

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 is dangerous. Disable it. More info here.

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.

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

Hide PHP version

To finish, a nobrainer:

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

. . . . . .