Once upon a time in the Linuxland...
Most of the web servers out there run on Linux or another UNIX variant, such as Solaris, FreeBSD, etc. One common theme among all UNIX variants of operating system is the security model of the file system, based on users, groups, ownership and permissions. This is a big topic, but a quick Google search on the topic would reveal a lot of great resources. Anyway, I'll try to give you the executive summary.
UNIX variants have users, each one belonging to one or several groups. Each file and directory on the file system is owned by one and only one user and group. The owning user doesn't necessarily belong to the owning group, which allows for flexibility. The system knows who is permitted to do what on each directory and file by means of permissions. Permissions are usually written down as three digit octal numbers. The first number tells the system what the owning user can do, the second one tells it what the owning group can do, and the third number tells it what everybody else (the rest of the “world”) can do. The commonly used numbers are 4 (read only), 5 (read and execute), 6 (read and write) and 7 (read, write and execute). The execute bit has a special meaning on directories: it means “browse”, i.e. allow someone to produce a list of the directory's contents. This is different than “read”, which simply means allowing the user to read files from that directory if and only if he knows their name.
But how does the operating system know how to parse these permissions? Just like directories and files, each program you run is owned by a user and a group. Apache — the most commonly used web server — is also a program. It runs “under” a user and group. So does the FTP server. To make things more complicated, the FTP server actually runs under the user you used to log in to it.
So, there you have it. The operating system knows who asks for access based on the user and group of the program which requests the access. It also knows what kind of access you request (read, write or execute/browse). When it finds the file or directory it can look up its owning user and group, as well as its permissions. It will first check if the owning and running user match. If they do, it will use the first number of the permissions to decide what to do. If not, it will check if the owning and running groups match and use the second number of the permissions to decide what to do. If all else fails, it will use the third number of the permissions (the “world” permissions) to decide. This is an elegant and effective system, until someone tries to use it unintelligently.
Taking a bite of an apple is all it takes
Most shared hosts are configured to run PHP as an Apache “module” (a.k.a. mod_php). This means that your PHP files run under the same user and group as your web server. Usually, these are called “nobody” and “nogroup”. However, when you upload your files with FTP, the FTP server runs under the user you used for logging in. Let's say that your user name is “foobar” and you belong to a group named “users”. You now have all the directories and files you uploaded with FTP owned by foobar:users and run under nobody:nogroup (do note that I use the shorthand notation of user:group for the rest of the article). This means that under the usual default directory and file permissions of 644 your PHP scripts (including Joomla! and its extensions) can't write to files on your site, or even list the contents of your own site's directories!
Why should you care? Well, modern web applications — such as Joomla!, WordPress and Drupal — need to be able to write to files for several reasons: cache files, software installation, logs, uploaded files through a web interface, backups, etc. Since the owning and the running users and groups differ, the only way to assign adequate permissions is to enable read, write and browse/execute access using the third permissions number, the infamous “world” permissions. A lot of instructions — and even software — suggest that you should use 777 permissions. Indeed, that works well and your PHP files can now write to files. However, it actually works a bit too well for your site's well-being. It's like taking a bite of the forbidden apple. How bad can it be?
You know the story. One bite of the apple is all it takes. You see, on a shared host, you are not the only user. Other sites also have a user on the same system. To make things worse, the exact path (a.k.a. absolute file system path) to each site's root is very predictable, usually in the format /home/username/public_html. If someone wants to hack your site, all he has to do is create an account on the same server and try to write to one of your site's files. With the 0777 permissions, HE CAN DO THAT EVEN THOUGH HE DOESN'T HAVE YOUR USER NAME AND PASSWORD! Remember how many times I've referred to the third number of the permissions as the “world” permissions? They truly live up to their name. They allow anyone — and I mean anyone — on the same system to write to your directories and files!
Adding insult to injury, a clever hacker doesn't even have to create an account on the same server. Your server runs a lot of software, such as a web server, an FTP server, a DNS server, an SSH server, a mail server and so on. If one of them is vulnerable, he can exploit them to write to your site's files. Even if you lock everything down, shared hosts pose a wonderful opportunity to screw you without even knowing about it. Even though you have taken all measures to avoid exploits being able to run on your site, another site hosted on the same server might not be so keen on security. If the other site is exploited, the hacker will be able to hack your site because the 777 permissions allow him to do that.
In other words, giving 777 permissions is the quickest path to allowing potential hackers to “own” your site with at least three different ways. Do you believe me now when I tell you that 777 is the number of the beast?
As a side note, I'd like to let you know of an exception to this rule. If any directory above the one you gave 0777 permissions has a 0 in the world permissions, e.g. 0700 or 0750, your operating system will not grant world-writeable privileges to your directory, despite having 0777 permissions. For instance, if your site is located in /home/myuser/public_html and /home/myuser has 0700 permissions, trying to set 0777 permissions in /home/myuser/public_html/tmp will have no effect whatsoever. This may explain why on a properly setup host giving 0777 permissions apparently does nothing.
Time to call an exorcist... or maybe not?
There are different ways to expel the evil 777 permissions off your server. It all depends on how your web server is set up. Fasten your seatbelts, we're entering the do-it-yourself zone! Remember: before you attempt any change on a live site, take a backup. Download it locally and store it in at least three different media. Never, ever, under any circumstances whatsoever attempt doing changes on a live site unless you have a valid and tested backup. You can take a backup manually, or using one of the free extensions in the Joomla! Extensions Directory.
The perfect shared host runs on suPHP
If unsure, there's an easy way to figure out if your host runs on suPHP. Go to Joomla!'s administrator back-end and click on the Help, System Info menu item. If the “Web server to PHP interface” reads CGI or FastCGI there's a good chance that your host is using suPHP. Just ask them.
The layman's shared host
Some shared hosts deny to use suPHP. These are the kind of hosts which cram 2,000 - 3,000 sites on a single server. suPHP comes with a performance hit, so they don't use it in order to be able to overcrowd their servers without bringing them to a screeching halt. Anyway, even in this case you can do something to semi-protect your site.
The common and wrong approach is to make all your files and directories owned by the web server user. The easy way to do that is taking a backup of your site, erasing everything from your account, then restoring the backup by extracting it with a PHP script which doesn't make use of FTP to transfer the files. All your files will be owned by the web server's running user and you can use 0644 permissions for files and 0755 for directories. From a security perspective, this is a big load of cow dung. Unfortunately, even seasoned professionals with a vast Linux experience don't avoid this pitfall. Why a pitfall? Ha! If another site hosted on the same server gets hacked, the hacker can write to your site's files, as the owning user of both sites is the same: the user and which the web server is running. Congratulations, you're hacked.
The correct approach is painful and time consuming. You need all your files and directories owned by your account's user (i.e. use FTP to upload them, or use a PHP script to extract a backup archive which can make use of FTP to write the extracted files) except those you need to be able to write to. Since we're talking Joomla! here, do the following:
Remove the cache, tmp and logs directories
Create the cache, tmp and logs directories using either of these file managers. These directories are now owned by the web server user.
Give them temporarily 777 permissions, again using these file managers.
Use your favourite FTP program to create a .htaccess file inside each of these directories with the following contents:
order deny, allow
deny from all
Give that file 0644 permissions using the FTP program.
Go back to the file manager (eXtplorer or NinjaXplorer) and give the cache, tmp and logs directories 0700 permissions. Note: you may need to give 0744 permissions to the cache directory and remove the .htaccess file for some CSS/JS aggregation & compression plugins to work. I consider this an awful practice, but it seems to be the only workaround... If you ask for my opinion, uninstall the plugin in question.
Go to Joomla!'s Global Configuration and enable the FTP layer. Do not store your password!
The trick here is multi-faceted. For starters, we made sure that the cache, tmp and logs directories are owned by the web server user, therefore they can be written to by Joomla!. Since these directories contain file which must not be accessible from the web, we used the .htaccess file to ban web access to them. Because the .htaccess was uploaded by FTP it is now owned by your account's user and its permissions do not allow it to be overwritten. Even if a hacker tried to exploit our site by writing files to those world-writeable directories, he wouldn't be able to execute them and his attack would fail. You also need to be able to write to other directories during components installation. That's why we enabled the FTP layer. Not storing the FTP password will cause Joomla! to request it every time it needs it. Since it's not written on any file on your site, even if your site gets exploited, the hacker won't be able to retrieve your passwords. Mission accomplished, with extreme prejudice!
The performance tuned dedicated host
That's my favourite part, because the widely adopted form of this kind of hosting represents a double failure.
When you have a heavily trafficked web site you need to run it on a dedicated server. Since it's a dedicated server, it implies that there is only going to be one site running on it. However, you want to put every CPU cycle to good use, therefore you can't use suPHP due to its overhead and incompatibility with performance tuning tricks, such as using op-code caches (e.g. APC – the Alternative PHP Cache). This is all good and sweet, until you decide to install ISPConfig, Plesk, cPanel or any other pre-packaged server environment manager (SEM) on the server.
Why? Because the moment you do that, you enter “dumb mode”. Your brain shuts down and accepts the setting “imposed” to you by the SEM. More specifically, all SEMs suppose that you are setting up a shared host and configure Apache to use a different user than the owning user of the one and only site you'll be hosting. As a result — and because you can't use the FTP layer due to the huge performance impact — you start giving 777 permissions.
Argh! You let the beast creep in! You do host only one site, therefore a hacker would have to penetrate this site's security to hack your site. Or not. He could use one of the various system servers (FTP, mail, SSH, DNS, etc) as an attack vector. Your 777 permissions give him the right to do so. Once more, you've made yourself a sitting duck. Why, oh, why do that? The solution is so simple.
No matter which SEM you use, you are the system administrator. You can edit Apache's configuration file and do something magical. Configure Apache to run under the same user as the owning user of the one and only site you're hosting. That's right. It's that simple. From that point, you can simply use 0700 permissions for directories and 0600 permissions for files. As Julius Caesar would have put it: veni, vidi, vici.
We are all humans and we are all subject to mistakes, even in the area of security. Being an expert doesn't help with completely avoiding mistakes. As I was writing this article I came to question my own security practices. You know what? I have screwed up on some sites and I have made a pretty good job setting up others. It's impossible to be perfect, let alone be perfect all the time, but it helps having it all written down. You can use this article as a reference for the next time you'll be setting up a client's site, or even your own site.
Remember that security is like contraception. It's optional, but only if you don't mind the consequences of an unfortunate intercourse with the wrong person. On the web it's even worse, as the wrong person will not even ask you. In the end of the day, it's entirely your responsibility.
Be well. Be safe. And don't let any software decide for you that 777 permissions are anything but evil!