Hi Niki,
The principle to work by here is 'least required access'. There's two functional types of users we care about, the one executing the PHP code (probably apache or php-fpm) and admins like yourself with FTP/shell access. Upstream wordpress documents application write requirements at https://codex.wordpress.org/Hardening_WordPress#File_Permissions - read it to know where the web server will expect write access, but don't follow the instructions - especially the numbers for chmod - by rote!
On Sat, Dec 2, 2017 at 3:30 AM, Nicolas Kovacs info@microlinux.fr wrote:
Hi,
Until a few months ago, when I had to setup a web server under CentOS, I assigned (I'm not sure about the correct english verb for "chown"ing) all the web pages to the apache user and group. To give you an example, let's say I have a static website under /var/www/myserver on a CentOS server running Apache. Then I would configure permissions for the web content like this:
# chown -R apache:apache /var/www/myserver # find /var/www/myserver -type d -exec chmod 0750 {} ; # find /var/www/myserver -type f -exec chmod 0640 {} ;
Some time ago a fellow sysadmin (Remi Collet on the fr.centos.org forum) pointed out that this is malpractice in terms of security, and that the stuff under /var/www should *not* be owned by the user/group running the webserver.
Right, this gives Apache write access over *everything*. That means that Apache could potentially change your site code. Many attack vectors rely on changing wordpress files or creating new files, so this should not be possible.
Which means that for the static website above, I could have something like this, for example:
# chown -R microlinux:microlinux /var/www/myserver # find /var/www/myserver -type d -exec chmod 0755 {} ; # find /var/www/myserver -type f -exec chmod 0644 {} ;
Or even this:
# chown -R nobody:nobody /var/www/myserver # find /var/www/myserver -type d -exec chmod 0755 {} ; # find /var/www/myserver -type f -exec chmod 0644 {} ;
I don't like the convention of creating an arbitrarily named user to own website files. Nicolas is logging in and working on the server, make an ie nkovacs user for yourself to do your work. Shared hosting companies tend to follow the "one FTP user named after website" or "one shell user named after customer" model and expect their customers to share a single login account, but if you have root access to the server there's no restrict yourself this way. It also leads to a solution where a group of folks who need to work on the site will share the single login account, making it impossible to answer questions like "who changed this file" or "who is logged in right now". If any kind of compliance is a concern, generic/anonymous login is a no-go. If compliance is not a concern, there's still no real benefit to making up usernames for yourself on a production system that are not your own name, and sharing credentials is still bad practice in principle.
Now I'm hosting quite a few Wordpress sites on various CentOS servers. Some stuff in Wordpress has to be writable by Apache. If I want to keep stuff as secure as possible, here's the permissions I have to define.
# cd /var/www # chown -R microlinux:microlinux wordpress-site/ # find wordpress-site/ -type d -exec chmod 0755 {} ; # find wordpress-site/ -type f -exec chmod 0644 {} ; # cd wordpress-site/html # chown -R microlinux:apache wp-content/ # find wp-content/ -type d -exec chmod 0775 {} ; # find wp-content/ -type f -exec chmod 0664 {} ;
As far as I know, this is the most secure setup for Wordpress as far as permissions are concerned.
Wordpress plugins are in wp-content. Allowing a wordpress plugin to be compromised is functionally equivalent to allowing the core code to be compromised, we do not want Apache to write plugin code. `wp-content/uploads` is the only *stock* directory I'm aware of that Wordpress *requires* write access too. Some plugins might have additional directories they write to, this should be documented for each such plugin.
With an application like Wordpress, Apache only needs to create files for things like images uploaded for posts. It should never be allowed to write in a directory where PHP files are. Conversely, any directory where it *can* write should not be used for PHP code. You can block that with the snippet below, again from upstream wordpress:
<Directory "/var/www/myserver/wp-content/uploads"> # Kill PHP Execution <Files ~ ".ph(?:p[345]?|t|tml)$"> deny from all </Files> </Directory>
You might notice that I used a <Directory> block where the page I linked to does not. The upstream example has you drop a <Files> block into a .htaccess file; in that context, the <Directory> is implicitly inherited from the immediate parent directory of the .htaccess file. It's a convenient way to adjust Apache configuration if you do not have privileged shell access, but it also means the .htaccess file will be read and interpreted anew for *every request*. You *do* have privileged shell access, and can put the directives into the config file that's read once, when the server loads, to avoid much recurring IO expense.
The problem is, I can't use automatic updates anymore. Whenever Wordpress releases a new version, I have to set permissions temporarily like this:
# chown -R apache:apache /var/www/wordpress-site
Then I can launch the update from within the Wordpress dashboard. And once the update is complete, I have to redefine sane permissions as above. Which is quite a bit tedious if you have two dozen Wordpress sites to manage, even if you have little scripts to define the permissions.
So I'm finally coming to my question. How problematic is it really to have the apache user and group owning the stuff under /var/www? I admit I followed the users' advice out of respect for his competence. But as far as I know, sometimes you get security advice where the resulting hassle far outweighs the real benefits.
Any suggestions?
Cheers,
Niki
--
You're describing Wordpress's http direct filesystem access method. There should be an option to chose another method. You can install the ssh2 php module and use the ssh access method, or the FTP access method. This way, when you update, you would enter your linux user credentials into Wordpress's update form and it would work because 'nkovacs' owns those files,or is a member of the owning group. If you chose FTP, do not allow public access, because FTP transmits credentials in the clear. Admins with real FTP clients can use sftp, which comes with the openssh server for free. With ssh or ftp update methods, Apache *never* gets write access to PHP files directly, it acts as an [s]ftp client to do the update work.
When 'nkovacs' creates a new file, it will be owned by nkovacs:nkovacs and inherit octal permissions from the parent directory and umask. That might break some of our assumptions about the role played by group membership, so make the group ownership 'sticky' with `chmod g+s -R /var/www/mysite/`. Now, new files will inherit group membership from the parent directory instead of the creating user.
If 'nkovacs' is the only admin who will ever update wordpress, or work with the site in a shell or FTP session, you can put 'apache' in the group owner slot, and set the octal permissions on directories to appropriately restrict it - ie 2755 everywhere except `wp-content/uploads`, which could be 2775.
If others will also perform those tasks, then you need a user group for them, ie 'microsites_devs' or 'mysite_admins'. We still want sticky groups, but we can't use the group slot to give Apache permissions anymore. Those image uploads are coming from the public internet, so it's not *too* much of a stretch to use the 'other' slot to allow apache writes via a 2777 mode - but this violates the 'least required privilege' principle because any user on the system, not just apache, can tamper with the files. A judicious application of facls fits this use case, ie
setfacl -m u:apache:rwX -R /var/www/mysite/wp-content/uploads setfacl -m d:u:apache:rwX -R /var/www/mysite/wp-content/uploads
The 'X' sets only directories for execute, in contrast to 'x' which makes files executable too. The first invocation applies to existing files and the 'd:' sets the 'default' ACL inherited by new files.
Also, don't forget that /var/www by default has the SELinux context httpd_sys_content_t, which will not allow writes regardless of octal permissions or ACLs. Wrap up by changing the context of directories you've determined should be writeable:
semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/mysite/wp-content/uploads(/.*)?" restorecon -R /var/www/mysite/
TL;DR my process is: - Make a list of real humans that need to work on the site - Assume the web server user should have at least read access on all files in the site documentroot, or we'd put them somewhere else. - Make a list of directories (uploads, cache, session files, etc) the web server must have write access to. - Use various permissions utilities to make sure humans and web server can do their assigned work and nothing more.
The first three steps are basically requirements gathering; for best results, don't skip ahead to applying permissions changes until you've established what permissions are needed.
HTH,
-- Pete