There is no point setting up something if it’s not secure. There are millions of bots who scan Magento stores for vulnerabilities. Set up your Magento store securely and you will have no outages caused by hackers. You will also have a good profit from your secure store.
This guide is for those people who know how to set up servers properly. This implies that:
- You have taken advantage of having a VPS or dedicated server
- You are using Nginx for hosting your Magento installation
- You’ve configured PHP-FPM to run as Magento files owner, i.e.
foo
system user (a.k.a the website user) - You’ve configured the
nginx
system user (a.k.a the webserver user) to be a member of website user’s group
Secure file permissions for a Magento website
Ownership (chown
)
Everything should be owned by the website user: i.e. chown -R foo:foo
.
In the standard setup, files are uploaded via SFTP by the website user.
PHP-FPM runs under that same user, so the website owner can always access the website files.
And thus, the PHP engine can access them too.
That is, provided that the site user will not revoke his own permissions for security or by mistake.
The website user can always read all the website’s files in this setup.
However, incorrect chmod
settings might prevent it and pose a security risk.
The following are the rules for the secure chmod setting of website files.
Rule of thumb for the owner chmod bit, (the first octal number)
The corresponding letter for this chmod bit is u=...
.
For directories
- Set owner permission bit to 7 for directories which need write access for the website user (PHP-FPM)
- Set owner permission bit to 5 for directories which require read-only access for the website user (PHP-FPM)
For files
- Set owner permission bit to 6 for files which require write access by PHP-FPM
- Set owner permission bit to 4 for files which require read-only access by PHP-FPM
Rule of thumb for group chmod bit, (the middle octal number)
The corresponding letter for this chmod bit is g=...
.
For directories
- Set group permission to 5 (i.e. 750) on directories that have static assets (images, CSS, Javascript files, etc.)
- Set group permission to 0 (i.e. 700) on directories that don’t require direct access by the webserver (contains PHP files only)
For files
- Set group permission to 6 (i.e. 640) on files that require webserver (not PHP) write access (mostly never use this)
- Set group permission to 4 (i.e. 640) on files that have static assets
- Set group permission to 0 (i.e. 600) on PHP files
Rule of thumb for the other chmod bit, (the last octal number)
The corresponding letter for this chmod bit is o=...
.
Set it to 0 always!
Example
We have a directory with static files only: /var/www/example.com/httpdocs/News
- We want the website user (PHP) to be able to read and write to that directory because we want to manage it via PHP file manager.
So the owner bit is 7 for the directory - We want web server user to be able to read files there directly, without going through PHP engine (just serve the jpg files)
So the group bit is 5 for the directory
We got chmod 750 for the directory (remember the last one is 0 always)
We apply the same rules from above for the files within that directory, and we get the correct chmod for them: 640.
Secure Magento after going live
For Magento 1.x website with standard directory structure, the following may be applied after the website goes to production.
Note! This is “lockdown” chmod – the website user won’t be able to write to the Magento core for security.
For more information read the manual.
The only major additions from the manual that we have here are giving read access to group (webserver) for media directory (since web server runs under a different user)
The following commands will cause a period of downtime while running because we start by applying the strictest permissions and later relaxing them with each command.
Also, NGINX might have to be restarted if open file cache is enabled (it might cache inaccessible status while the commands are running and visitors accessing).
We start from 400
(files) and 500
(dirs), which is equivalent to u=rX,g=,o=
.
This permission allows only reading all directories and files to the PHP-FPM user.
Then we level up these permissions where needed, using letter syntax of chmod
as well.
Letter chmod convention is the most useful for gradual permission increase:
# initial chmod:
chmod -R u=rX,g=,o= .
# in media directory, PHP-FPM should be able to create files (e.g. image upload), and NGINX to read them:
chmod -R u+w,o+rX media
# in var directory, PHP-FPM typically would create files, but there is nothing for NGINX to serve/read from there, thus:
chmod -R u+w var
chmod u+wX includes
chmod u+w includes/config.php
# Cron script should always be executable
chmod +x cron.sh
chmod g+r favicon.ico sitemap.xml
chmod -R g+rX errors errors/default
chmod -R g+rX errors/default/{css,images}
find js ! -name '*.php' -exec chmod g+rX {} \;
find skin ! -name '*.php' -exec chmod g+rX {} \;
# finally make sure that directory itself is accessible to nginx (group):
chmod g+rX .
The X
letter in chmod
application commands stands for applying execution(traversal) permission only to directories.
The lockdown chmod
, in general, is prone to NGINX error:
rewrite or internal redirection cycle while processing “/index.php” is possible with this config
See related post about it for adjusting your NGINX to be more friendly to lockdown chmod
s.
Our lockdown permissions are extreme because we whitelist a handful of directories and files accessible to NGINX.
Everything else is unreadable for security reasons, which is a great security measure. Overall we have ensured that:
- PHP-FPM is allowed to write only to the
var
directory and toincludes/config.php
file - PHP-FPM is unable to change core files – if a malicious file is executed, it won’t be able to affect the core files
- The webserver (NGINX) is allowed to read static files
Naturally, if there is an additional directory with images, CSS files, etc. i.e., treat it as media
:
chmod -R u+w,o+rX <some other dir with static files>
To identify such directories which contain static files, run:
find . -type f -name '*.jpg' - name '*.css' -printf '%h\n' | sort -u
The only downside of the above approach is the actual downtime during its application.
To make for a no-downtime lockdown (which might result in a less secure chmod
overall), you should “inverse” the commands to mostly remove permissions instead of setting exact ones.
First, set permissions to the ones allowing both NGINX and PHP-FPM users to read the files, then gradually make them more strict:
chmod -R u=rwX,g=rX,o= . # this sets dirs to 750, files to 640 (allow write to PHP anywhere, NGINX read everywhere)
chmod g= includes # disallow NGINX from reading the includes dir
find . -type f ! -name 'index.php' -name '*.php' -exec chmod u-w,g= {} \;
chmod u-w,g= cron.sh # same
chmod -R g= var # there is nothing for NGINX to read there
Note that for PHP files we disallow the website user from modifying them, by removing the write permission (-w
).
NGINX user is furthermore restricted to not being able to even read those files with the exception of index.php
files.
This is an important gotcha. For directives like index index.php
, NGINX has to check the files for existence.
Alternatively, you can
Lock down chmod of PHP files (most important)
We finalize by preventing PHP from changing our chmod back.
This needs to be run by a sudo user:
sudo find . -type f -name "*.php" -exec chattr +i {} \;
On websites with multiple SSH users per site
Run commands above first.
Then, before accessing Magento via browser for the first time, run:
find var/ -type d -exec chmod g+s {} \;
find media/ -type d -exec chmod g+s {} \;
chmod g+s includes
chmod g+s includes/config.php