I have been doing web development since, well, web development basically began. And I've used a wide range of hosts. Since I don't see anyone stating answers succinctly and definitively anywhere, it is time to write a solution to the question on everyone's mind: What are the permissions that I should set for web server directories and files?
The first step is to identify the user that the web server will access files with/run under. For example, many Linux distributions set up 'www-data' as the user. I'll be focusing mostly on Linux as it powers about 66% of the Interwebs, but Windows Server users can benefit too.
It is important to get your setup correct from the very beginning. Propagating permissions down the website tree as new directories and files are created is critical to maintaining sanity. Knowing who created a specific file or directory is also important when working in a team. As always, if you can't trust other users who might have access to the files on the system, then you should not be using that system. This is especially true of shared hosting and so you need a VPS, cloud, or a dedicated host if you want to do web server operations correctly and securely.
Going back to the example for Linux, this is the best solution I've come up with that minimizes the number of times ownership has to be adjusted by an admin/sudo/root user:
The 'g+s' sets the sticky bit for the group, which propagates the group AND the permissions down the tree as new files and directories are created. This allows all users in the group to freely edit those files and create new directories without having to constantly use the command-line to adjust ownership.
Putting 'cron' scripts in the web root is just asking for trouble unless you carefully restrict each script to only run from the command-line (it can be accomplished though). It's just easier to have a separate directory that's outside the web server's document root for executable code that cron or custom services that the OS runs (e.g. Cloud Storage Server /scripts).
Alrighty, that's about all on this topic. And now you know!
The first step is to identify the user that the web server will access files with/run under. For example, many Linux distributions set up 'www-data' as the user. I'll be focusing mostly on Linux as it powers about 66% of the Interwebs, but Windows Server users can benefit too.
It is important to get your setup correct from the very beginning. Propagating permissions down the website tree as new directories and files are created is critical to maintaining sanity. Knowing who created a specific file or directory is also important when working in a team. As always, if you can't trust other users who might have access to the files on the system, then you should not be using that system. This is especially true of shared hosting and so you need a VPS, cloud, or a dedicated host if you want to do web server operations correctly and securely.
Going back to the example for Linux, this is the best solution I've come up with that minimizes the number of times ownership has to be adjusted by an admin/sudo/root user:
- Create a new group (e.g. 'addgroup sftp-users' on Debian/Ubuntu).
- Add users to the group who will be allowed to upload files to the web server via SFTP (e.g. 'adduser youruser sftp-users' on Debian/Ubuntu). No one should be using FTP anymore.
- Create a directory for the web server files (e.g. 'mkdir /var/www').
- Adjust permissions (e.g. 'chown root /var/www', 'chgrp sftp-users /var/www', 'chmod 775 /var/www', and 'chmod g+s /var/www').
- Create a directory for cron/scheduled custom scripts to run (e.g. 'mkdir /var/scripts').
- Adjust permissions (e.g. 'chown root /var/scripts', 'chgrp sftp-users /var/scripts', 'chmod 770 /var/scripts', and 'chmod g+s /var/scripts').
The 'g+s' sets the sticky bit for the group, which propagates the group AND the permissions down the tree as new files and directories are created. This allows all users in the group to freely edit those files and create new directories without having to constantly use the command-line to adjust ownership.
Putting 'cron' scripts in the web root is just asking for trouble unless you carefully restrict each script to only run from the command-line (it can be accomplished though). It's just easier to have a separate directory that's outside the web server's document root for executable code that cron or custom services that the OS runs (e.g. Cloud Storage Server /scripts).
Alrighty, that's about all on this topic. And now you know!
Hi,
ReplyDeleteThanks for this great article. However I am a bit confused as I don't see the web server group and user (www-data) set anywhere and how the web server user is supposed to be able to serve the pages.
The developers need permissions to be able to edit files. I think that bit is what is there in the article. But the server needs to serve the files too. So please elaborate a bit about that too.
Thanks loads
You never want 'www-data' to own the directories except for handling file uploads. And even then you want to be extremely careful about what can be uploaded. When 'www-data' owns a directory, that allows any process running as the web root to write files into the web root. That's how you get into compromised server territory. If someone can upload a .php file and can get the server to execute that file, then it's game over and your website security is toast. Today's attacker will at least abscond with your databases if not take over the entire server (database credentials are usually stored as plaintext somewhere in a config file). And just because you think you are only allowing image uploads doesn't mean someone hasn't configured their web server properly. For example, the Nginx developers made it easy to badly configure a server to upload a file as a .jpg that is really a .php file and then craft a URL that causes Nginx to pass the .jpg off to PHP-FPM, which will then execute the file as PHP.
DeleteIn the world of *NIX (Linux, Unix, BSD, etc), directories and files have three sets of standard bits: rwxrwxrwx. R = read, W = write, X = execute (files) or list (directories). The first set of 'rwx' is owner, the second 'rwx' is group, and the third 'rwx' is "world" (i.e. any process). Each process on the system runs as a specific user and group. Apache/Nginx typically run as 'www-data' for the user and 'www-data' for the group. So the web server can read and write to any directory owned by 'www-data'. The web server (and any process running under it) can also read any directory and file that has world-readable permissions.
Back to this post, the directories end up with 775 + setgid (rwxrwsr-x) and files end up with 664 (rw-rw-r) and both files and directories propagate 'sftp-users' for the group (thanks to the setgid bit). Any process on the system can read the directories and files. Any user account that is a member of the 'sftp-users' group can create directories and write files. The user will own the files but the entire group can modify freely because of the g+s propagation down the directory tree.
I'm not sure about involving the server's root account in this process? I'm curious about that aspect of the idea...
ReplyDeleteThe default is usually `www-data`, since that's the account that the webserver is usually setup to use upon installation... So I would think that somewhere in the equation the webserver user would come up, whether it's `www-data`, `apache`, etc... For example, if there is a folder that needs write access for uploads or logs... I would think that if the webserver is running as `www-data`, then changing the directories to `root` it would cause issue? Is the webserver is running as `root` in this scenario?
Making a directory owned by root guarantees that it is owned by no one else. The root user is the ultimate authority on a proper *NIX system. Permissions on the directory allow group and world access to it.
DeleteNo, there is no issue with root being the owner as long as the directory is world readable, which it is. See my reply above to Ap Seh. Ownership of /var/www by root makes the most sense. The setgid propagation allows users who SFTP/SSH into the system with the 'sftp-users' group to freely create and edit directories and files. In addition, the owner of the file is going to be the user who created the file. However, anyone in the 'sftp-users' group can edit the file because what gets propagated by 'rwxrwsr-x' on the directory is 'rw-rw-r--' on files, with the exception of the root user, which has a default umask that only propagates 'rw-r--r--'. So, basically, use a decent SFTP client for creating and editing files and don't create and edit files on the system as root unless absolutely necessary.
One thing I did not mention in the post is that the 'www-data' user should NEVER be added to the 'sftp-users' group. That would be a very bad idea. Only real humans with SSH/SFTP rights to the system should ever be added to the 'sftp-users' group.
The web server is NOT running as root here. That would be incredibly bad.
Actually, web servers DO run as root during initial startup (that's how system services work). The master process that forks out child processes might continue to run as root but child processes run as or switch to the configured user/group before accepting network connections.
All of the web servers I operate use this configuration and the number of permissions related issues that come up are quite rare (usually someone did something over SSH as the root user when they shouldn't have in a way that requires a root user to go in and clean up the mess). The setup described in the post is secure and should really be the default when a web server is installed from a package manager (e.g. apt, yum). Finally, using chmod 777 is an indicator of a bad server configuration, which is what a lot of people have been trained to do and one of the reasons why we see daily data breaches popping up in the news.
"If the web server use needs to be able to write to a specific directory within the tree structure, it should, first and foremost, be its own isolated directory (e.g. 'uploads'). Then change ownership to the web server user (e.g. 'chown www-data uploads'). I've never had a legitimate need to set 'chmod 777' on a directory or file with this approach.
ReplyDeleteThe 'g+s' sets the sticky bit for the group, which propagates the group AND the permissions down the tree as new files and directories are created. This allows all users in the group to freely edit those files and create new directories without having to constantly use the command-line to adjust ownership."
Thanks for your article, Thomas.
I am a newbie left with a couple of questions:
(01.) Are you saying from your 'mkdir uploads' prompt that this directory should be in the user's home directory? If not where?
(02.) In the next paragraph, you speak of 'g+s', which permissions / attributes you have not given to this directory unless it is contained within the web document root or it's sub-directories, which you suggest it (uploads) should not be.
I would be much more comfortable if I could be more confident in exactly what you mean, please? - Bin
What I mean is that any directory within the web root that is writeable. You should NEVER make a directory writeable by the web server user that contains application code (e.g. PHP files). Doing so allows code running as the web server user (e.g. PHP) to replace files within the directory (i.e. allows for difficult to clean up malicious code deployments). You want directories to be isolated that the web server user can write to. I gave a file upload directory as an example. A lot of directions on the Internet say to 'chmod 777' a directory or a file to allow an application to write to it. 'chmod 777' is basically guaranteed to be incorrect permissions whenever you see it. A lot of popular web software also gets this wrong (e.g. the installer and the self-upgrader for WordPress). I even got it wrong for a while until I figured out the g+s propagation trick.
DeleteFor the second paragraph, I was addressing the person's question regarding the use of 'g+s' and what it does. In regards to an upload directory, g+s is still important. You want the 'www-sftp' group to propagate everywhere in the web root so that the users in the group still retain control over all of the files via SFTP.
One note about uploads: You should consider where uploaded files are actually stored on disk. If you need control over file accesses by users or don't want a user to share a direct URL, then uploaded files shouldn't even be in the web root but located elsewhere on disk. Then, for example, a PHP script acts as middleman to control access to files but delivery of the actual file data can be done optimally via server modules and headers such as Xsendfile. In the instance where the uploads directory is publicly accessible, you also have to take care to prevent users from uploading malicious things like their own PHP files but renamed with a '.jpg' extension and then executing those files via a URL as PHP files (e.g. an improperly configured web server). When a server is compromised via a public file upload directory, it's then a simple matter to dump database tables (i.e. a data breach), changing the site to be a malware host, or any number of other nefarious activities. You only really hear about the big server compromises in the news. However, Internet-facing servers are compromised daily around the world because of seemingly insignificant things like directory permissions.
However, a good starting point is to create your 'uploads' directory and then 'chown www-data uploads'. It's ownership will change to 'www-data' but you should still be able to create files inside the directory over SFTP because the group on the directory is 'www-sftp'. Then, in your code that processes file uploads, be sure to validate that user uploads are actually valid files (i.e. don't just look at the file extension or the MIME type and assume all user input is malicious until proven otherwise). That's going to cover most use-cases. You only need to really care about putting files in a secure location when bandwidth leeching is a concern or when working with Personally Identifiable Information (PII data) such as PDFs of signed invoices/receipts, tax info, and other financial documents.
Good explanation, but I was confused.
ReplyDeleteSetting the GUID flag (chmod g+s) on a directory does not affect the mode of nested dirs and files. New or existent nested dir/files only inherits the owner group.
There are two ways to have 775 mode for dirs and 664 for files.
1) set user umask to 0002. Either globally (/etc/login.defs) or per user (~/.profile). This makes all dirs/files everywhere created by this user(s) to have 775/664 mode
2) use ACL: setfacl -m d::rwx /var/www . Before that /var/www should already have 775 mode. This will make all new created dirs/files only inside /var/www to have mode 775/664
Propagating the group is the most important aspect of this post. Setting 775/664 consistently is much less important. If two people and some file are in the same group, they both have control of that directory or file regardless of the mode. Some software, especially WinSCP, ignores umask and sets its own mode in an, IMO, nicer way but it's not necessary. The goal is to reduce/eliminate the number of 'sudo chmod/chown/chgrp' commands required on the host and making a mess of the directory structure and ruining system security in the process. Users can chmod directories and files to their heart's content without elevating to root.
DeleteI do assume that it is a NEW web server in my post. Existing directories and files won't be affected. Fixing that requires manual changes to be made.
The only times I've been bitten with g+s issues is as root. If root creates a file, it ignores g+s and does its own thing instead of propagating the parent directory group. Which is actually to be expected as you probably don't want to risk accidentally creating an executable file that someone could write to as another user in the same group and then have it later execute as root (e.g. resulting in privilege escalation for other users on the system).
g+s is setgid (or sometimes SGID), not GUID. A GUID is something entirely different. :)
Thanks for the tips. I'm sure someone will utilize them.