Apache And Web Content Permissions

Home » CentOS » Apache And Web Content Permissions
CentOS 13 Comments


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. 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 {} \;

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. 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?



Microlinux – Solutions informatiques durables
7, place de l’église – 30730 Montpezat Site : https://www.microlinux.fr Blog : https://blog.microlinux.fr Mail : info@microlinux.fr Tél. : 04 66 63 10 32

13 thoughts on - Apache And Web Content Permissions

  • I would build a rpm package of wordpress (everything can be defined there like permissions etc)
    and disabling the automatic update function in wordpress. Build once it can be installed on all
    (two dozen) webservers automagically (local yum repository) …

  • Am 02.12.2017 um 14:27 schrieb Nicolas Kovacs :

    The application design should have considered security best practices. I do not known WP but check their sites.
    So, following the “need to write” requirement, its a good decision (yours) to allow only the minimum. “Normally”
    such space should be outside of the “document root” of the hosting.

    All installations should have the same base (normally the latest WP release) – so, to be clear one package for all. This has nothing to do with different content or themes. I other words, if security is your focus then the process is the target of your effort.

    Its just my suggestion …


  • I think very, especially when running something like wordpress. WP
    has (had) a history of some rather serious security issues. Even if they are resolved, having the user that runs the apache server own
    (or even have write access to) the directories and files that it has access to leaves you totally vulnerable to someone breaking through the server. I’m not so worried about apache proper (though struts was the equifax vector apparently) but more about any scripting that one may have on the site. All it takes is a bad script or two for your site to be totally taken over.

  • Oh, we all read (only) what we want :-)

    “problematic” should be defined by yourself (probability * impact = risk).

    to answer lets use a comparison: the root user can write to all /bin/ files. Executing them will not change the binaries (in a perfect world). What happens when something tries to use this fact (write perm) to do malicious things?
    Therefore its good practice to work as “non-root” user. So, when the httpd user (web daemon) has full write permissions, what happens when something tries to use this fact (write perm) to do malicious things?
    Anybody that have an eye on the httpd logs knowns that the web is not a perfect world. Not an direct answer because there is not an absolut one but I hope that I could express my point of view …


  • Le 02/12/2017 à 16:25, Brian Mathis a écrit :

    This is EXACTLY what I’ve been looking for. I’ve spent a few hours experimenting with wp-cli, and the big advantage is you are supposed to run it as the user owning the web content, so no need to fiddle with permissions, even temporarily.

    I’m currently writing a detailed blog post about this, since it looks like this makes my life much easier. Thanks very much !

    Cheers from the sunny South of France,


    Microlinux – Solutions informatiques durables
    7, place de l’église – 30730 Montpezat Site : https://www.microlinux.fr Blog : https://blog.microlinux.fr Mail : info@microlinux.fr Tél. : 04 66 63 10 32

  • 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!

    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.

    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.

    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:

    # Kill PHP Execution

    deny from all

    You might notice that I used a block where the page I
    linked to does not. The upstream example has you drop a block into a .htaccess file; in that context, the 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.

    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
    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.


    — Pete

  • Le 03/12/2017 à 23:59, Pete Travis a écrit :

    Wow. Thanks *very* much for your detailed answer. I’ll work through that this week.

    FYI, I’m the only user with shell access to the server. The user
    ‘microlinux’ is my “standard” non-root user on the server. I know I
    could also have called him ‘nkovacs’.

    When hosting WordPress (or Dolibarr, OwnCloud, …) I don’t expect my users to do administrative tasks, because that’s precisely my job. They’re only expected to *use* this stuff (e. g. write a blog, do their management, share files over the network, etc.). And no, I don’t use FTP, only SSH (mostly with key authentication).



    Microlinux – Solutions informatiques durables
    7, place de l’église – 30730 Montpezat Site : https://www.microlinux.fr Blog : https://blog.microlinux.fr Mail : info@microlinux.fr Tél. : 04 66 63 10 32

  • Le 02/12/2017 à 16:25, Brian Mathis a écrit :

    Here’s a little script I wrote to automatically update WordPress core, extensions and themes. Works perfectly. I’ll run it manually for a few days, then I guess I’ll define a cronjob for it.

    –8< ------------------------------------------------------------- #!/bin/bash # # wordpress-update.sh # # Mise à jour automatique de toutes les installations WordPress # # (c) Nicolas Kovacs

    # WP-CLI doit être installé

    # Apache HTUSER=’apache’

    # Utilisateur normal WPUSER=’microlinux’

    # Racine du serveur Web WPROOT=’/var/www’

    # Identifier les installations WordPress WPDIRS=$(dirname $(cd $WPROOT && find . -type f -name ‘wp-config.php’))

    for WPDIR in $WPDIRS; do
    cd $WPROOT
    # Définir les permissions correctes
    find $WPDIR -type d -exec chmod 0755 {} \;
    find $WPDIR -type f -exec chmod 0664 {} \;
    chown -R $WPUSER:$HTGROUP $WPDIR/wp-content
    find $WPDIR/wp-content -type d -exec chmod 0775 {} \;
    find $WPDIR/wp-content -type f -exec chmod 0664 {} \;
    cd $WPDIR
    # Mettre à jour le moteur WordPress
    su -c “$WP core update” $WPUSER
    # Mettre à jour les extensions
    su -c “$WP plugin update –all” $WPUSER
    # Mettre à jour les thèmes
    su -c “$WP theme update –all” $WPUSER

    exit 0
    –8< ------------------------------------------------------------- Cheers, Niki -- Microlinux - Solutions informatiques durables 7, place de l'église - 30730 Montpezat Site : https://www.microlinux.fr Blog : https://blog.microlinux.fr Mail : info@microlinux.fr Tél. : 04 66 63 10 32