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
Top comments (7)
Nice list. I was only following 6 of these, and now I think I should look for implementing remaining 4.
In
open_basedir
,/tmp
is used for multi-part uploads and archival functions. So maybe that was your reason to add it.Yes, probably ;)
Securing a php app :)
step 1 :->
step 2 :->
Re write application in whatever else :)
Just kidding, excellent post!
secureDevops <3
Haha, made my day.
If that's the actual line you're using for your site, then your
disable_functions
substitution has a typo in it:ignore_user_abord
should beignore_user_abort
.YES! Great catch, thank you! :D
Wow. I only had 5 of these implemented. You = lifesaver. Thanks.