Last updated April 9, 2014. Created by lsrzj on April 10, 2008.
Edited by authentictech, gisle, LeeHunter, decibel.places. Log in to edit this page.

The server file system should be configured so that the web server (e.g. Apache) does not have permission to edit or write the files which it then executes. That is, all of your files should be 'read only' for the Apache process, and owned with write permissions by a separate user.

As a quick test to confirm whether your site is secure or not you can run the Security Review module. However, this module may from time to time report false positives. To avoid being confused by these you need to check the module's issue queue to see if there any open issues that is about false positives.

Note that this whole article is about "defense in depth." Drupal can run quite safely with permissions a little "looser" than they should be. But if an administrator account is compromised by an attacker or an attacker gains the ability to execute arbitrary code then the configuration below will limit their ability to further exploit your site.

Configuration examples

For example, on many systems the Apache process runs as a user called "www-data" that is in a group called "www-data". This user should be able to read all of the files in your Drupal directory either by group permissions or by "other" permissions. It should not have write permissions to the code in your Drupal directory. If you use features of Drupal which require the "files" directory, then give the www-data user the permission to write files only in that directory.

The following is an example file listing of a safe configuration showing two files in a site where uploaded files are stored in the "files" directory. In order to see the file permissions set for your setup, go to the command line and type: ls -al.

drwxrwx---  7 www-data    greg-group  4096 2008-01-18 11:02 files/
drwxr-x--- 32 greg-user   www-data    4096 2008-01-18 11:48 modules/
-rw-r-----  1 greg-user   www-data     873 2007-11-13 15:35 index.php

In the above example, the web server user has the ability to write in the files directory, any users in the group "greg" can read and write the data as well, but other users are not allowed to interact with that data. The "index.php" file (representative of all code files) can be edited by "greg" and can be read by the www-data group (we assume the www-data user is in the www-data group). No other users can read that file. This is a fairly secure method of configuring your site. You generally don't want random users who have the ability to read files on your server to see inside those files, hence the last three permissions are --- instead of r-x.

Below is an insecure setup:

NOTE: THIS IS AN INSECURE CONFIGURATION
drwxrwx---  7 www-data  www-data  4096 2008-01-18 11:02 files/
drwxrwx--- 32 greg-user www-data  4096 2008-05-16 11:48 modules/
-rw-rw-rw-  1 www-data  www-data   873 2007-11-13 15:35 index.php

This configuration allows the www-data user to edit the index.php file (and since it is representative of other files, we assume every file that makes up the Drupal site). This configuration is dangerous for the reasons outlined below. If you are using a configuration like this, please change it immediately to be more like the first version. Instructions for making the necessary changes are given below for Linux Servers.

Quick lesson in permission's numeric equivalents

On Unix-like servers (including Linux), permissions for files and directories can be specified using letters (e.g., "r" for read, "w" for write, and "x" for execute bits) or numbers. The numbers 4, 2, and 1 are conversions of these bits from binary, and correspond directly to read, write, and execute. The instructions below use the alphabetic notation because it is easier to read, but you often will see permissions given in numeric notation in the Drupal Handbook and many other How-Tos. Below are examples of the alphabetic notation and the numeric equivalent:

rwx  ==  111 binary  ==  7
r--  ==  100 binary  ==  4
---  ==  000 binary  ==  0

Unix filesystems assign permissions to three different categories of possible users -- owners, members of a security group defined on the server (e.g., using the groupadd command in Linux, the pw command in FreeBSD or the dseditgroup command in OSX), and everyone else. Each of these categories is assigned permissions using the alphabetic or numeric notation explained above via the chmod command.

The permissions for a file or directory can be displayed using the ls -l command. The display shows file or directory permissions in the first column in alphabetic notation. The first character indicates the type of directory item, with a "d" whether the file system entity is a directory; a "-" is shown if the entity is a file. The next three letters show the permissions for the user-owner of the file or directory. The next three letters show permissions for the group-owner of the file or directory. The last three letters show the permissions for everyone who is not the user-owner or a member of the group that owns the file or directory. For example:

drwxr-x--- 10 joe     www-data    4096 Oct 15 14:15 ./
drwxr-xr-x 13 root    root        4096 Oct 11 14:50 ../
-rw-r-----  1 joe     www-data    6553 Aug  1 12:27 authorize.php
-rw-r-----  1 joe     www-data   70700 Aug  1 12:27 CHANGELOG.txt
-rw-r-----  1 joe     www-data    5267 Oct 12 22:47 .htaccess
drwxr-x---  4 joe     www-data    4096 Oct 15 14:23 includes/
-rw-r-----  1 joe     www-data     529 Aug  1 12:27 index.php

So, in the listing above, the index.php file is owned by the user joe and the group www-data (usually the name of the Apache user and group on Debian-based systems). The permissions allow joe to read and write the file but allow users in the www-data group only to read the file (except joe, of course, who has owner permissions).

Addendum on chmod Non-Numeric Permission Notation

When used in the chmod command, the symbols below have the meanings given:

"+" = add a permission to the ones already assigned
"-" = revoke a given permission maintaining the others already assigned
"=" = ignores the already assigned permissions and assigns the permissions specified
"u" = user
"g" = group
"o" = others
"a" = everybody / all (user, group, others)

For files:

r = read
w = write
x = execute

For directories:
r = list (read directory contents)
w = write
x = can access the directory (i.e., cd to the directory)

Using chmod without numeric values makes it more human-readable. These are some examples of how to use it:

chmod commands and results for a file with permissions rwxrwx--- (770)
chmod human chmod numeric resulting permission
ugo=rwx 777 rwxrwxrwx
u-wx 470 r--rwx---
o+r 774 rwxrwxr--
g-wx,o+r 744 rwxr--r--
u-w,g-wx,o+r 544 r-xr--r--
g=,o=r 704 rwx---r--
a-wx 440 r--r-----

Note: Because of the way Unix filesystems work, all files and directories created by the Apache server will be created with an owner that is the same user as is running httpd. In Linux, you can handle this with a useful tool called fsniper. It uses iNotify to check for newly created/modified directories and files and you can apply whatever action you want through customized scripts on them in real time. So you can create scripts to change ownership of files and directories automatically. This solution doesn't work (and isn't required) in Microsoft Windows because of NTFS "permission inheritance".

TIP: The following comand may also deal with different 'x' for files and dirs:

[root@localhost]chmod go+rX

The uppercase X will add the execute permission only to directories leaving regular files untouched. Both files and dirs would get read permission added.

How insecure permissions are a problem

If you allow your site to modify the files which form the code running your site, you make it much easier for someone to take over your server.

Worst case scenario: a file upload tool in Drupal allows users to upload a file with any name and any contents. This allows a user to upload a mail relay PHP script to your site, which they can place wherever they want to turn your server into a machine to forward unsolicited commercial email. This script could also be used to read every email address out of your database, or other personal information.

Undesirable scenario: if the malicious user can upload a file with any name but not control the contents, then they could easily upload a file which overwrites your index.php (or another critical file) and breaks your site.

Undesirable scenario: if the code allows users to see the contents of files, attackers could see information which might reveal potential attack vectors.

If you are a sysadmin

Disclaimer: Don't risk following these instructions blindly; each system has its own peculiarities and, because of that, the instructions here MUST be altered to suit your needs. All of the instructions here are aimed at people who are familiar with filesystem permissions and know exactly what all the commands written here mean. If you try to follow these instructions without paying full attention to what you are doing, you are very likely to get into trouble. These instructions are designed to alter filesystem permissions as a root-level user, so tread carefully!

Note for hosted Drupal installations

This installation method presumes one shared drupal core and many subdomains installed under it. So suppose you are the owner of a hosting service that has a Drupal installation already pre-configured and you want to sell a hosted Drupal site with pre-configured Drupal core to your clients. So when you sell it, you create their user on the server and let them configure their own Drupal site just entering their site's address to open the install page. The way this document suggests the configuration, it will prevent customers from modifying and accessing the Drupal core files and other customer's sites files and directories.

It is important to notice that the user ownership of Drupal's core directories/subdirectories and files is given to the user who administers Drupal (usually root) and group ownership is given to the group your apache is running on. For files and directories inside the "sites" directory the user who is hosting the site on your server is their owner. One way to do this is to delete the "sites" directory under Drupal's root directory and make it a symbolic link to /home or another path you use to store user's home directories. That way, if you create user names that matches the customer's site URL, no permission will need to be changed. The only thing to be done is to change the directory group ownership to the group your apache is running on.

cd /path_to_drupal_installation
mv sites/* /home
rmdir sites
ln -s /home sites
useradd www.example.com
chown -R www.example.com:www-data /home/www.example.com

The permission required by Apache is given through group permission and others have no access at all to any files and directories on a Drupal installation. Don't give any permissions to "others", otherwise if your system is hacked through a user's weak password, the hacker will be able to access all files and directories of all installed sites on your server, not only the one invaded. Even a read-only permission must be avoided. Remember that the user name and password to connect to the database for each site are stored in settings.php. Worse still, if you give write permission to others, the hacker will be able to alter files to damage your site or upload malicious scripts to your server.

The instructions in this guide assume a non-hosted installation, so modify the ownership to match your situation as necessary, e.g. where you see "greg" in these instructions, replace "greg" with the name of the user (often root, but not necessarily) who administers your installation of Drupal.


Linux servers

Permissions for files and directories on a Linux system are adjusted using the chmod command. User-owner and group-owner identity for files and directories are adjusted using the chown command.

The code below demonstrates one method for changing the ownership and permissions of files and directories in the Drupal Root directory to conform ownership and permissions to the recommendations above. We assume in the example below that the user greg is part of the greg group and that user greg is the site owner. We also assume that you are running Drupal on a server that is not in a hosting environment which provides website hosting services to multiple customers.

Make sure you run the following commands from inside Drupal's root directory! If you run these commands from any other directory, you either will not make changes to all of the Drupal installation's files and directories or you will make changes to files and directories other than those in the Drupal installation. Neither alternative is your goal.

[root@localhost]cd /path_to_drupal_installation
[root@localhost]chown -R greg:www-data .
[root@localhost]find . -type d -exec chmod u=rwx,g=rx,o= '{}' \;
[root@localhost]find . -type f -exec chmod u=rw,g=r,o= '{}' \;

The second command makes user greg the user-owner and group www-data the group-owner of all files and directories in Drupal's root directory and all subdirectories and files in those subdirectories (the -R switch means recursive). Note that in a multiple-customer hosting environment, the user-owner of the Drupal files and directories should be root.

The third command in the example finds all directories and subdirectories in Drupal's root directory and executes the chmod command on all of those directories and subdirectories (-type d means filesystem entities that are directories). The command changes the permissions to read, write and access for user greg and read and access for users in the www-data group. Users who are not greg and not in the www-data group cannot read, write, or access the directories or subdirectories in the Drupal root directory. In numeric notation, the permission assigned to these directories and subdirectories is 750.

The fourth command finds all files in the Drupal root directory and its subdirectories and changes the permissions on those files to read and write for the user greg and read only for the www-data group. Other users have no access to these files. The numeric notation for this set of permissions is 640.

For the "files" directory in the sites/default directory and any other site directories in a multi-site installation, the permissions are slightly different because the www-data user must have write permission to the directory:

[root@localhost]cd /path_to_drupal_installation/sites
[root@localhost]find . -type d -name files -exec chmod ug=rwx,o= '{}' \;
[root@localhost]for d in ./*/files
do
   find $d -type d -exec chmod ug=rwx,o= '{}' \;
   find $d -type f -exec chmod ug=rw,o= '{}' \;
done

The second command above finds all subdirectories named files below the sites directory and changes the permissions for the user-owner and the group-owner to read, write, and access. All other users cannot read, write to, or access these files subdirectories.

The "for" loop above is written for an sh-style shell (sh, bash,ksh). If you use csh or tcsh, type bash before executing the command. These commands in the loop give read, write, and access permissions to user greg and group www-data to all subdirectories and files within the files but not access to other users. The numeric permissions code is 770.

Remember that any newly installed module/theme or whatever add-on must have its permissions changed too. It's better to do this BEFORE installing the module, theme, or add-on in its appropriate Drupal directory.

Windows servers

By default, Apache runs in the built in SYSTEM account. So all you have to do is change the permission in a way that only the SYSTEM account, the administrators group and the user greg have access to the Drupal root directory, subdirectories and files (assuming greg is the site owner).

You should exclude all other users and groups from the ACL. For the SYSTEM account give read only access to all Drupal directories, subdirectories and files except for the "files" directories which require write permission. For the user greg give read, modify and write permissions. And for the administrators group give complete access. Go to the Drupal root directory, right click on it, go to properties and then security.

Make use of permission inheritance to make things easier for yourself. And remember that any newly installed module/theme or add on must have its permissions changed too. It's better to do this BEFORE installing it in its appropriate Drupal directory. Permission inheritance is done automatically by Windows.

Special considerations for settings.php

The settings.php file contains the database password and username in plain text and, once created, must be set so that only appropriate users can read it. That usually means removing read permissions for the "other" user.

Summarizing the permissions

  • drupal_admin: the user on the server that administrates Drupal, not necessarily is the root.
  • site_admin: the owner of the hosted site (a customer)

Ownership

Core modules/themes files and directories: drupal_admin:www-data
Hosted sites modules/themes/files files and directories: site_admin:www-data

Permissions

Core modules/themes directories: rwxr-x---
Core modules/themes files: rw-r-----
Hosted sites modules/themes directories: rwxr-x---
Hosted sites modules/themes files: rw-r-----
Hosted sites "files" directory: rwxrwx---
Hosted sites files under "files" directories: rw-rw----
Hosted sites subdirectories under "files" directories: rwxrwx---

Follow this guide exactly as it is to make your Drupal installation as secure as possible. This guide was tested and works. If something goes wrong with your installation, review the steps — possibly you missed something. If it really doesn't work, post a comment with your issue, and someone will fix this guide.

Script based on guidelines given above

If you need to fix permissions repeatedly then the following script will help you, it is based on the guidelines given above and performs some checks before any modification to ensure it is not applied on files/directories outside your Drupal installation.

#!/bin/bash
if [ $(id -u) != 0 ]; then
        printf "This script must be run as root.\n"
        exit 1
fi
drupal_path=${1%/}
drupal_user=${2}
httpd_group="${3:-www-data}"
# Help menu
print_help() {
cat <<-HELP
This script is used to fix permissions of a Drupal installation
you need to provide the following arguments:
1) Path to your Drupal installation.
2) Username of the user that you want to give files/directories ownership.
3) HTTPD group name (defaults to www-data for Apache).
Usage: (sudo) bash ${0##*/} --drupal_path=PATH --drupal_user=USER --httpd_group=GROUP
Example: (sudo) bash ${0##*/} --drupal_path=/usr/local/apache2/htdocs --drupal_user=john --httpd_group=www-data
HELP
exit 0
}
# Parse Command Line Arguments
while [ $# -gt 0 ]; do
        case "$1" in
                --drupal_path=*)
drupal_path="${1#*=}"
;;
--drupal_user=*)
drupal_user="${1#*=}"
;;
--httpd_group=*)
httpd_group="${1#*=}"
;;
--help) print_help;;
*)
printf "Invalid argument, run --help for valid arguments.\n";
exit 1
esac
shift
done
if [ -z "${drupal_path}" ] || [ ! -d "${drupal_path}/sites" ] || [ ! -f "${drupal_path}/core/modules/system/system.module" ] && [ ! -f "${drupal_path}/modules/system/system.module" ]; then
printf "Please provide a valid Drupal path.\n"
print_help
exit 1
fi
if [ -z "${drupal_user}" ] || [ $(id -un ${drupal_user} 2> /dev/null) != "${drupal_user}" ]; then
printf "Please provide a valid user.\n"
print_help
exit 1
fi
cd $drupal_path
printf "Changing ownership of all contents of "${drupal_path}":\n user => "${drupal_user}" \t group => "${httpd_group}"\n"
chown -R ${drupal_user}:${httpd_group} .
printf "Changing permissions of all directories inside "${drupal_path}" to "rwxr-x---"...\n"
find . -type d -exec chmod u=rwx,g=rx,o= '{}' \;
printf "Changing permissions of all files inside "${drupal_path}" to "rw-r-----"...\n"
find . -type f -exec chmod u=rw,g=r,o= '{}' \;
printf "Changing permissions of "files" directories in "${drupal_path}/sites" to "rwxrwx---"...\n"
cd sites
find . -type d -name files -exec chmod ug=rwx,o= '{}' \;
printf "Changing permissions of all files inside all "files" directories in "${drupal_path}/sites" to "rw-rw----"...\n"
printf "Changing permissions of all directories inside all "files" directories in "${drupal_path}/sites" to "rwxrwx---"...\n"
for x in ./*/files; do
find ${x} -type d -exec chmod ug=rwx,o= '{}' \;
find ${x} -type f -exec chmod ug=rw,o= '{}' \;
done
echo "Done settings proper permissions on files and directories"

Copy the code above to a file, name it "fix-permissions.sh" and run it as follows:
sudo bash fix-permissions.sh --drupal_path=your/drupal/path --drupal_user=your_user_name

Note: The server group name is assumed "www-data", if it differs use the --httpd_group=GROUP argument.

Looking for support? Visit the Drupal.org forums, or join #drupal-support in IRC.

Comments

re: find . -type d -name files -exec chmod ug+rwx,o-rwx {} \;

This only works for a new site (e.g. one that doesn't already have files in the /sites/[site]/files directory).
For the hardening of an existing site,`find . -type f -exec chmod u+r-wx,g+rw-x,o-rwx {} \;` sets all of the files inside sites/[site]/files to be read-only, and `find . -type d -name files -exec chmod ug+rwx,o-rwx {} \;` will only affect new files; anything currently existing in the /files folder will still be read-only. A `chmod -R ug+rwx,o-rwx /[path to site]/files/*` should fix this.

Thanks for the howto.

chmod -R ug+rwx,o-rwx /[path to site]/files/*

This command gives execute permissions for both the owner and group to all regular files residing within the /[path to site]/files/* tree? Isn't this a bad thing?

It's nuts that web server should own the site. In all normal cases your regular user (that you do your regular ssh, scp or ftp access with) should should own the installation files.

Anyway, this regular user should normally own an html area exclusively, from which the web server serves the pages of the host/virtualhost, that is only readable and searchable to others (i.e. the web server). After ensuring that this is the case the user then normally copies or uploads the contents of the distribution into the html area, thus maintaining ownership and sane permissions. The user then grant the web server read, write and search permission to the files directory of the site by changing the group access. Optionally, the user also gives up his own write permissions for the files directory (in order not to tamper recklessly with this area as it should be managed exclusively by the web server).

There's really no need for recipes, if the user doesn't grasp this he simply needs to do UNIX permission homework or else he'll never be secure. If that is the case, enter the following into shell or Google:

man chown
man chgrp
man chmod

Thanks for the advice. After reading your post, I found the following relevant information:
http://www.onlamp.com/pub/a/apache/2004/05/28/apacheckbk.html

"Apache runs as the user specified in the User directive. Files need to be readable by that user. Directories need to be +x (searchable) by that user. CGI programs need to be runnable (+x) by that user."

User apache is completely unpriviledged and has no login shell. A question, supposing that the file index.php in drupal's root have these file permissions: -r--r----- 1 apache apache 980 Mai 18 15:25 index.php. Would it be possible to an attacker explore an Apache vunerability to take advantage of the ownership by apache to execute a chmod on it and alter the permission in a way that it becomes writeable and then modify or overwrite it uploading a hacked one?

Shell or no shell, as long as the user the server and the php scripts runs as owns files and directories, it can also alter them. Owning files is a privilege. So if your server, or php, or Drupal has a hole, or most likely, if you accidentally allow someone to plant some php code, you're in trouble.

$ ls -l secretfile
---------- 1 www-data www-data 0 2009-08-20 21:10 secretfile
$ wget http://localhost/malign.php > /dev/null 2>&1
$ ls -l secretfile
-rwxrwxrwx 1 www-data www-data 0 2009-08-20 21:10 secretfile

Thank you for your clarification, when you told about this on the comment I became worried because I didn't think of this possibility. Now that you explained how it can be achieved I saw the problem and modified all my permissions in the server to u=rwx,g=rx,o= for directories and u=rw,g=r,o= for files and the ownership is site_owner:apache. This way only site_owner can chmod the files and directories.

Hi, permissions above look pretty tight. However it doesn't work ... at least for me (Ubuntu 8.04 x64 serv), I don't know which files have to be writable, executable etc ... but with these permissions I am getting only "Forbidden - u don't have permissions ....." to whole drupal subdirectory, when I set drupal directories to "chmod 1755" and files "chmod 1644" it's much better (perhaps sticky bit is not necessary), i can work in any case at it's very important for me (newbie in drupal). I would like to have secure site at the same time, therefore I tried permissions here from "wiki", i have experimented with them about 2 hours and ended up with working ones as I mentioned above. I haven't tried to edit .htaccess, perhaps there is also some possiblility to achieve usefull behavior.

----edit-----
as mike_waters points out directories need to be executable (x) so one has to # find . -type d -exec chmod u=rwx,g=rx,o=x {} \; this fix the problem, I should have read carefully his post. It could save me 3 hours of trying and googling ...

If you have read those 3 commands on the beginning of this howto carefuly you should have seen that for directories it has an x to permit access.

[root@localhost]cd /path_to_drupal_installation
[root@localhost]chown -R greg:www-data .
[root@localhost]find . -type d -exec chmod u=rwx,g=rx,o= {} \;
[root@localhost]find . -type f -exec chmod u=rw,g=r,o= {} \;

I needed to modify thos commands to
find . -type d -exec chmod u=rwx,g=rx,o=rx {} \; - that's 755
find . -type f -exec chmod u=rw,g=r,o=r {} \; - that's 644

Otherwise my Bluehost hosted Drupal site shows Forbidden when I try to access it.

--------------------------
Drupal projects:
http://MalinaMusic.ru
http://ephototips.com

Other prijects:
http://malina.mobi

I am using Hostgator and the documented code broke my site. Being a newbie I panicked because I didn't know what files and folders I just changed. Luckily, I found this post and was back up and running.

It is important to note that I could not execute the first line:
chown -R greg:www-data .

I omitted it when after realizing that chown was not permitted and gave me an invalid user error. Perhaps this is part of my problem.

Small side note, I experimented with:
find . -type d -exec chmod u=rwx,g=rx,o=r {} \;

and my site still worked but had no styling. I suppose there is a use for that down the road.

Hey, it's wrong giving to others any kind of permission and the user greg, if you read carefully, was used as an example and you MUST change it for the proper user that administrates Drupal(usually root or your user). Look at this: "To set the permissions all at once you can issue these commands from the Drupal root directory. I'm assuming that greg user is part of greg group and that greg is the site owner." It´s just an assumption just to explain a situation not an obligation. READ CAREFULLY AND REPEAT THE STEPS, YOU SURELY MISSED SOME STEP!

This documentation is for site administrators, newbies must know deeply about Linux permissions scheme before using this guide.

I followed the instruction above and the entire site broke. I was having problems with a theme, thought I should go ahead and verify permissions across the entire site. Running your permissions got my site working again.

The only thing worse than no documentation is documentation that doesn't work.

Hey, it's wrong giving to others any kind of permission and the user greg, if you read carefully, was used as an example and you MUST change it for the proper user that administrates Drupal(usually root or your user). Look at this: "To set the permissions all at once you can issue these commands from the Drupal root directory. I'm assuming that greg user is part of greg group and that greg is the site owner." It´s just an assumption just to explain a situation not an obligation. READ CAREFULLY AND REPEAT THE STEPS, YOU SURELY MISSED SOME STEP!

This documentation is for site administrators, newbies must know deeply about Linux permissions scheme before using this guide.

Awesome, these two lines are just what I needed to get my site running up at GoDaddy. Sheesh, what a frustrating week it's been. Thanks!

ACLs are an under-used feature of Linux. It's great for web development, as the server can have one set of permissions while the developers have their own. However, I don't think Drupal supports this, because it thinks that the server has write permissions to a directory that it doesn't have write permissions to.

Being brand new to Drupal, I could be wrong, but I believe I'm interpreting the example below correctly.

Status Report:
Configuration file Not protected
The directory sites/default is not protected from modifications and poses a security risk. You must change the directory's permissions to be non-writable.
Directory Permissions:
elizabeth:/home/www/faba3.v.cryptomeme.com/webroot/sites$ ll
total 8.0K
drwxrwx---+ 2 elizabeth staff 4.0K 2009-09-16 12:40 all
drwxrwx---+ 3 elizabeth staff 4.0K 2009-11-17 15:27 default
elizabeth:/home/www/faba3.v.cryptomeme.com/webroot/sites$ getfacl default/
# file: default/
# owner: elizabeth
# group: staff
user::rwx
group::rwx
group:www-data:r-x
mask::rwx
other::---
default:user::rwx
default:group::rwx
default:group:www-data:r-x
default:mask::rwx
default:other::---
elizabeth:/home/www/webroot/sites/default$ ll
total 28K
-rw-rw----+ 1 elizabeth elizabeth 9.3K 2009-11-17 15:27 default.settings.php
drwxrwx---+ 2 elizabeth staff     4.0K 2009-11-17 15:26 files
-r--r-----+ 1 elizabeth staff     9.3K 2009-11-17 15:27 settings.php
elizabeth:/home/www/webroot/sites/default$ getfacl files/
# file: files/
# owner: elizabeth
# group: staff
user::rwx
group::rwx
group:www-data:r-x
mask::rwx
other::---
default:user::rwx
default:group::rwx
default:group:www-data:r-x
default:mask::rwx
default:other::---

Now for the "files" directories the permissions are slightly different because it must have write permission to the www-data group too [run the following from the Drupal root folder]:
find . -type d -name files -exec chmod ug=rwx,o= {} \;

This command is not so secure because some contrib modules contain 'files' directories such as feedapi, and in core we now have ./modules/simpletest/files

Also, it does not cover the case where you have existing files in it the 'files' directory. I can't find a better way other than running the following commands from each sites/SITENAME/files directory:

cd /sites/SITENAME/files
find . -type d -exec chmod ug=rwx,o= {} \;
find . -type f -exec chmod ug=rw,o= {} \;

I'm happy to update the documentation with this, but maybe someone has a better way of doing this?

Why not to do something like this:

for d in sites/*/files; do
  find $d -type d -exec chmod ug=rwx,o= {} \;
  find $d -type f -exec chmod ug=rw,o= {} \;
done

How is this better?

What you are saying is correct. Other modules may contain files directory. Giving apache write permissions to this files directory is a security threat.

Your commands are perfect.

I've added an uppercase x execute permission tip that applies only to directories.
HTH.

Many of us work through FTP programs, and not the command line.

Could you please post info on the numeric values corresponding to the CHMOD equivalents you listed here?

Thanks!

Like two_wheeler, I need to use the numeric values.

As I read (and re-read) the instructions in this document under Linux servers, and combine that with the information from Mike_Waters, I think this is what they mean.

For all directories and subdirectories in Drupal's root directory:
Symbolic notation: rwxr-xr-x
Octal numbers: 755
Which means:
  • Owner: read, write, execute
  • Group: read, execute
  • Others: read, execute
For all files inside Drupal's directories:
Symbolic notation: rw-r--r--
Octal numbers: 644
Which means:
  • Owner: read, write
  • Group: read
  • Others: read
For the "files" directory inside Drupal's "sites" directory:
Path: /sites/files or /sites/default/files
Symbolic notation: rwxrwxr-x
Octal numbers: 775
Which means:
  • Owner: read, write, execute
  • Group: read, write, execute
  • Others: read, execute
For all files inside Drupal's "files" directory and inside any subdirectories with the "files" directory
Path: /sites/files/*.html and all other files in files directory
Path: /sites/files/subdirectories/*.html and all other files in subdirectories
Symbolic notation: rw-rw-r--
Octal numbers: 664
Which means:
  • Owner: read, write
  • Group: read, write
  • Others: read

If someone in-the-know could confirm or correct this it would be a big help.

Thanks!

The handbook has been updated and I put a session explaining the chmod non numeric permission representation. If you carefully read the results of each command you'll see that others has no access at all to any file or directory contained in Drupal's installation directory and all the directory tree below it, all the other permissions you wrote are correct. If you write the commands suggested in this handbook, you'll have the best configuration for security. You can simply copy and paste them since you are inside drupal's root directory, if you are not, you'll mess all your filesystem permissions!

Great explanation of this confusing stuff, but please explain - is it necessary for any user (including the web server user) to have write permission on any files in the drupal directory at all?
I think the only necessary write permission is on the files directory and subdirectories, but not on files within these directories - is this wrong?
Similarly all directories should have execute permission for the web server and no files should have execute permission at all.
Then if you want to edit some files the write permission on those files, and the execute permission on the directory they are in, can be given
to the owner (and never the web server.)

with folder files/images;
If I set rwxrwxr-x and owner root I cannot upload images
# Directory "iimages" is not accessible under file system!
# Unable to get a working directory for the file browser

If I set rwxrwxrwx and owner root I can upload images
but the module alert that
Some files and directories in your install are writable by the server

If I set rwx------ and owner apache I can upload Image but always same alert's message

why cannot use this
rwxrwxr-x owner root ?

os centos and imce with fckeditor

other:
Security Review also alert regard html full, which html tags is preferable to remove from html full?

Usually, server run as user www-data or nobody. So if you have your files owned by root, only if "others" with the relevant permissions will be accessing them. Check owner and groups in the "Configuration examples" at the beginning of this post.

on CentOS (which many cPanel servers use), cd to the public_html directory if necessary, then:

chown -R nobody:nobody /sites/default/files

This will make the apache web server able to upload images including with ckeditor/imce

Just figured this out after a day of struggles hope it helps someone.

Thanks for writing this and everything, but all that I and 99% of people reading this page really want is a list of numbers next to a list of folders. This just confuses me and makes something really simple into a frustrating and confusing waste of time. Here is the permissions you should set for folders in Drupal 6.x, for those who don't feel like learning apache command line scripting just to set some permissions:

/default on 755
/default/files including all subfolders and files on 744 (or 755)
/default/themes including all subfolders and files on 755
/default/modules including all subfolders and files on 755
/default/settings.php and /default/default.settings.php on 444

By the way Jason, I followed your indications (thank you!) and it worked :)

I started with Drupal in 2007 and then my life got stuck...

Yes I would put this info at the top, rather than buried in the comments. This is the heart of what most people want to check, a very quick reference sheet, rather than a wall of text.
:-)

Merging Technology with Creativity
Fountain City Productions
http://fountain-city.com

This page is classic Drupal.org. The people who probably most need a tutorial are the people doing their first Drupal site. And these are the people (like me) who probably know how to use a ftp program to change settings and everything else on this page is Greek to them. Thanks Arman for some clear, concise, usable information.

broadway

I can't begin to tell you how quickly you resolved my can't write to files directory problem after migration with these simple cut and paste instructions:

[root@localhost]cd /path_to_drupal_installation/sites
[root@localhost]find . -type d -name files -exec chmod ug=rwx,o= '{}' \;
[root@localhost]find . -name files -type d -exec find '{}' -type f \; | while read FILE; do chmod ug=rw,o= "$FILE"; done
[root@localhost]find . -name files -type d -exec find '{}' -type d \; | while read DIR; do chmod ug=rwx,o= "$DIR"; done

Since our community has thousands and thousands of image files, what a time saver! This information helped me understand how to more tightly reign in permissions on files and saved the day for me.

A big thank you.

protoplasm

First of all: thanks for this doc, it's an important part of drupal's usage.

The 1st sentence is the heart of the matter: "system should be configured so that the webserver does not have permission to edit or write the files which it then executes".

'files' directory is an exception, that's why it is a security risk.
If someone can upload script (php) into that folder, your server is broken.

Let's minimize this case:
1.
files/.htaccess should not be writeable by webserver

2.
disable php in that folder, add this line to your files/.htaccess:

AddHandler default-handler php

(Your server will be safer, if a security hole or a weak admin password allows to upload php files.)

A great idea, but if you grant write permission to the "files" directory itself, that permission could be used to overwrite any .htaccess file within, regardless of the permissions on that file (at least on some OSes). So to really accomplish this, you'll need to put the directives into your main Apache site config. Something like this:

<Directory /var/www/pathtositeroot/files>
  SetHandler default-handler
  Options None
  AllowOverride None
</Directory>

I have some of my sites that have user and group set to apache for everything / all files and dirs. Is this bad? Under the impression that user apache is a special user with very limited permissions.

Also I run my own server so don't need a user for each site I host so I assume if I need to have the user not be apache I can just create a user eg webdude and use them for all my sites or is this less secure?

You must create it and you must use this user. If a user is the file and directory onwer all limitations go to the ground. You simply can put an script to change permissions and ownerships as you want, hackers will do it undoubtedly

From a serverfault thread
To see if your webserver can write to anything that it's not supposed to, if you have sudo, you can run

sudo -u www-data find . -type f -writable | grep -v sites/default/files

- this will list anything writable by the apache user - it should be empty after your site files dir is excluded.
It will just list, not fix it for you (the commands in the OP will do that for you).

on centos would that be: sudo -u apache find . -type d -perm /u=w | grep -v sites/default/files ?

Joel Box - Mondial-IT

Hi lsrzj, would you please confirm my translation of your documentation and your script per below:

I need to use the numeric values to apply permissions via FTP for a site hosted on Bluehost.

I reviewed the security script suggested in the document, under the heading: "Script based on guidelines given above". I am yet to test this but I believe it means:

1. [@ ROOT] DRUPAL CORE PLUS OUR ADDED MODULES & THEMES

  • All Core Drupal Folders and Sub-Folders = 750 (rwx,r-x,---)
  • All Core Drupal Files and Files in subfolders = 640 (rw-,r--,---)
  • Our own modules/themes/libraries use the same lockdown permissions.
  • /sites/all/themes = as above
  • /sites/all/modules = as above
  • /sites/all/libraries = as above

2. [@ /SITES] DRUPAL SETTINGS

  • /sites/default Folder = 755 (rwx,r-x,r-x). Personally I think this should be = 750 (rwx,r-x,--) if it works.
  • /sites/default/settings.php = 444 (r--,r--,r--). Personally, I think this should be = 440 (r--,r--,---) if it works?

3. [@ /SITES/.../FILES] WRITEABLE FILES in /sites/all/default/files folders : (like images, file attachments etc)

  • All Folders in /sites/all/default/files and all its subfolders : 770 (rwx,rwx,---)
  • All Files in /sites/all/default/files and it subfolder files : 660 (rw-,rw-,---)

these folders/files are the exception so that Drupal can delete the files attached to posts when they are deleted etc.

4. REDUCING WRITABLE FILES VULNERABILITY
Addtitional recommendation by: herend on February 24, 2011 at 11:57am
"...system should be configured so that the webserver does not have permission to edit or write the files which it then executes". So the server will be safer, if a security hole or a weak admin password allows to upload php files.

1. files/.htaccess should not be writeable by webserver
2. disable php in that folder, add this line to your files/.htaccess:
AddHandler default-handler php

I hope this is a correct translation and likely to work! I will investigate and attempt. Any confirmation from lsrzj or others, that this looks sensible, would be appreciated.

I have a test site setup automatically with user=me group=me. How is www-data not even factored into this and the site works just fine. According to this tutorial. apache needs to have at least read access to the files but the www-data group/user has no permissions. Only my user/owner has permissions.

All I did was download drupal and extract it and the site works fine. Working with all the permission setting above gives me all types of problems. Such as the login page not being responsive.

There are at least *three* pseudo-groups that have their own permission set. 'owner', 'group', and 'other'
It would be normal for a host that set your user up with a web directory to assume that you wanted stuff you put there to be world-readable, so the 'other' permissions probably include the normal 'read-only' permissions, and www-data can read and serve your files.

There are now a number of extended file system properties that allow even more control than that, but in this case, they are probably not an issue.

I wonder why there were no a single word about Linux ACLs while they do really good job.
Recently I was trying to setup permissions/owns for a website to provide access to another developer and faced the fact that technically it is impossible to achieve the result that would secure enought and flexible using old-style linux fs security. I will demonstrate what I mean.

Suppose we have next setup:

Users:
customer - site's owner
dev1, dev2 - two developers
apache - apache user

Reqs:
1) customer, dev1, dev2 - should have r/w access to the site
2) apache - should have r/w access to the site's /sites/all/files dir only
3) any changes to site's files shouldn't break the requirement 1-2. For example, files created by apche should be r/w accessible to customer, dev1 and dev2 and vice versa.
4) nobody else should have access to the site

I'm gonna skip considering different "tranidional" approaches to achieve above, including running patched apache which can set specific owner/owning group (vie AssignUserID for example), for only reason - it would be either impossible or ugly to the extreme.

Now I'll show how to do this with simple native Linux ACLs. Let us assume the site root is under 'www' dir of the current directory and all the files are owned by the `customer` user and the `customer` group.

1) adding customer user and group to the "write list" for all the files:

setfacl -R -m user:customer:rwx, group:customer:rwx www
setfacl -R -d -m user:customer:rwx, group:customer:rwx www (-d stands for default perms - i.e. inherited)

2) adding dev1 and dev2 to the "write list" for all the files:

setfacl -R -m user:dev1:rwx, user:dev2:rwx www
setfacl -R -d -m user:dev1:rwx, user:dev2:rwx www

3) adding apache user to the "write list" for sites/default/files:

setfacl -R -m user:apache:rwx www/sites/default/files
setfacl -R -d -m user:apache:rwx www/sites/default/files

That's all. Note - not a single chmod/chown issued. Let us see file permissions now with example output from real setup (customer = mosaicum, dev1 = fantomas, dev2 = doka, apache as is):

-bash-3.2$ getfacl www
# file: www
# owner: mosaicum
# group: mosaicum
user::rwx
user:fantomas:rwx
user:doka:rwx
group::r-x
mask::rwx
other::--x
default:user::rwx
default:user:fantomas:rwx
default:user:doka:rwx
default:user:mosaicum:rwx
default:group::r-x
default:group:mosaicum:rwx
default:mask::rwx
default:other::--x

-bash-3.2$ getfacl www/sites/default/files
# file: www/sites/default/files
# owner: root
# group: root
user::rwx
user:apache:rwx
user:fantomas:rwx
user:doka:rwx
user:mosaicum:rwx
group::r-x
group:mosaicum:rwx
mask::rwx
other::--x
default:user::rwx
default:user:apache:rwx
default:user:fantomas:rwx
default:user:doka:rwx
default:user:mosaicum:rwx
default:group::r-x
default:group:apache:r-x
default:group:mosaicum:rwx
default:mask::rwx
default:other::--x

Advantages:

1) You don't need chmod/chown. You don't even need chmod g+s'ing
2) You don't need to reissue commands (if only some outdated software which doesn't respect ACLs will break them) - acls are inherited (thanks to "-d")
3) You ain't depending on who creates files no more - you just don't care about owners - and they now go like the should - preserving names. So for example if it was you, who last uploaded a new version of a file - then it will be owned by you and your group, but the staff don't lose control over it.

UPDATE:

* drush dl breaks ACLs since it moves (renames) downloaded and extracted files from /tmp to the site's dir (and while moving ACLs/owners/permissions never copied - this is "by design").
But thanks to drush developers, shell's rename is inside drush_move_dir() which has a workaround in case when system rename "doesn't work": files from /tmp are first copied to the site's dir and then unlinked from /tmp. When acting like this, ACLs are applied/preserved. To make system rename doesn't work you just add one line to either /etc/drush/php.ini or ~/.drush/php.ini:

disable_functions = rename

* tar zxf module.tar.gz would likely extract module which will become r/o for all the groups and named users. I think this is because tar preserves r/o for groups permission when creating archive and when extracting, this changes "mask" attribute of extended ACL to become "r-x" and not "rwx".

Well, I didn't find how to make tar to extract ignoring mode (or rather with mode changed to 666). But to change the mask back you can either use setfacl again, or just issue this right after tar zxf:

chmod -R g+w module

Great page but perhaps needs to make mention of the fact that any files uploaded will be owned by www-data:www-data..

Only ways I know of to get around this are..

1. Add the user to the www-data group - Problem here is that the user then has access to everything that www-data has access to potentially including other users files (if you have more than one user/site on the server.. If its a single site server this isn't such an issue)..

2. Set the UID on the writeable directories so that newly created files inherit the user..

3. Create a separate group and make www-data and the user members of the group.. Use this new group when setting permissions and set the GID of the writeable directories to the new common group..

Not sure which way is best or most secure but think that whichever is felt to be best is detailed on the page..

I vote for the second one!

How would you achieve #2? I thought unix file permissions weren't inherited without use of an insecure umask. Can you share more?

It works only for dedicated servers or VPS.

It is not useful at all for shared hosting solutions.

Apache should work with client username or user will not be able to even change or delete uploaded via apache files!
Plus many other problems, specially when user try to move from one web hosting company to other.

And I don't even talk about newbies! They will go nuts.

It works for hosted installation. Note that the OWNER of each site's subdirectories and files will be each client's system logins so if you have a site www.example.com and the hosting service create a user www.example.com, the owner of all files and directories inside it will be your user. The OWNER can do whatever he wants to do with the directory, move files, delete files.... the Apache permission is given through GROUP permissions, Apache MUST NEVER be the owner of the files. The problem is that apache will try to create the files and directories with it's user. So I'm open to suggestions to solve this issue, there must be a way to do it. Is there a point to install one apache for a single user and run multiple instances of it, each apache running under the costumer's user name?

If apache works from NOBODY (for example) - all created files and directories via drupal will be own by nobody.

User will not be able to chown it, because CHOWN restricted to root user only.
Also user will not be able to chmod it, because he is not own it.

When regular (no shell experience at all, no unix permissions knowledge ) user see this situation, he just put 777 on all dirs and files in sites/default/files.
And it is a big security hole.

For shared hosting there is no simple way to have php files owned by user, web host by apache and don't have any file permissions issues.

And don't forget. Shared hosting is mass hosting. Users pay only ~$5/month and they just need to see they own website online.
They don't want to play with permissions, owners and etc. Any limitations will push them away.

UPD: there is only way to process each request via user's id for apache. But it is mean, that user will be able to change any file.

I checked that there is a way to inherent parent directories permissions using POSIX ACLs, so using this, apache will create, automatically, files and directories owned by the users.

Sounds good.
But if php under apache works with apache user and I have admin role with a right to write node with php code - I can read any settings.php on all websites and hack few more websites then.
Safe mode will save from it, but it will make problems - http://drupal.org/node/324295.

There is another solution for this, not tested by me, but it seems to be very reasonable, it's to use an intelligent handler for files and directories that uses inotify to do whatever you want with newly created files or directories or modified ones. This tool is called fsniper, take a look at the readme, you'll need to download it. You'll see why I think it's a good solution, it's a very versatile tool. http://freecode.com/projects/fsniper

Thank you for good information about fsniper.
We use similar tool to backup added and modified files on backup server. It is safe time and server resources.

I think we should change permission and ownership restrictions for Shared hosting solutions, because there is no simple and good way to secure drupal on shared hosting.

Slight correction to your thinking..

Apache doesn't create or delete files.. PHP does..
Apache only needs read permission to files/directories..

Running PHP as the owner of the scripts can be accomplished in a number of ways.. Typically with apache modules like SuEXEC or SuPHP.. It can also be done with MPM-ITK or running PHP-FPM with individual pool configurations..

There are pro's and cons to all these options and some will need specific permissions and/or group memberships..

You will just have to research what is going to work for your particular setup..

Agree.
But then you will fail test from http://drupal.org/project/security_review with error "some of your files or directors in your install are writable by the server".
And recommendation will be a read this topic about permitions.
That's why I am here.

Yes, it all depends on how the review tool is configured and what its writer considers an adequate level of security..

Usability and security are opposite ends of the spectrum.. In general if you want all the functionality you sacrifice security and if you want security you sacrifice usability.. Taken to its logical conclusion the most secure server is one that's not connected to anything and powered off, obviously not very usable.. By connecting it to the internet and powering it on you have already introduced a number of security risks..

I guess what I am trying to say is that you need to work out the security level YOU are happy with after weighing up the risks and go from there.. Use security review systems purely as a guidance and make your own assessments..

I am totally agree with you.

Let me explain, why I say "this recommendation is not useful for shared hosting for Drupal."

I own my Hosting company for Drupal.

It is small and I cannot afford $5000/month for advertising on http://drupal.org/hosting.
I've tried to apply to "Other great hosts that support Drupal".

The part of this process is pass security test on hosting.
We run each apache fork with user permissions (personally I believe it is a best way to do it for many reasons of performance and security) and Drupal can overwrite themselves.
It is failure this test.

That's why I am trying to explain that this recommendation is good for VPS or Dedicated servers, but not for shared hosting.

If thats the requirement the only way to do it is have Apache/PHP access the files with a group permission and not the owner.. Set the group permissions to read only for all the files and only the "files" directory writeable.. Unfortunately this may mean some major changes to your server setup..

It means even more.

Drupal does not work with safe mode and safe mode has been DEPRECATED as of PHP 5.3.0 and REMOVED as of PHP 5.4.0.

And if apache (+mod_php) will work with user nobody.... I will be able to get access to any settings.php on whole server, change admin password for all of them and hack all this websites at one time!

UPD: And it will create one more hole in future. Almost 99% of clients don't understand linux permissions, that's why they will just put 777 on all files of drupal.

What is the correct permission for the current Drupal 7! Please someone write it here collectively for all Drupal root folder and files, customize modules and theme, for the default folder, for files folder and its subfolders and files. Please in numbers! This page is a million views of various ages, please summarize someone to date, thank you!

I read the previous comments, but I'm still confused... Is there a suggested configuration for files/folders permissions on a shared hosting?

Right now my files are on myusername:myusername ownership.
I don't have permissions to chown to myusername:nobody (nobody:nobody = apache), and I have set o+r (o+rw to 'files' directory), which doesn't make me feel comfortable.

Yes, there are. Look at If you are a sysadmin section.

Hello delxpez

sysadmin - mean you have a right to be root and you don't.
Basically there is no way to follow this recommendation on any shared hosting! It is my personal opinion.
All this words just for Guys who has root access!

I understood you, but this install method presumes one shared drupal core and many subdomains installed under it. So suppose you are the owner of a hosting service that has a Drupal already pre-configured and you want to sell a hosted Drupal site with pre-configured Drupal core to your clients. So when you sell it, you create their user on the server and let them configure their own Drupal site just entering their site's address to open the install page. The way we suggested the configuration, you will prevent costumers from modifying and accessing the Drupal core files and other costumer's sites files and directories.

Just curious on this part but shouldn't all of the find commands, for consistency purposes, have their {} wrapped in single quotes? So instead of:

[root@localhost]find . -type d -exec chmod u=rwx,g=rx,o= {} \;
[root@localhost]find . -type f -exec chmod u=rw,g=r,o= {} \;

It would look like:

[root@localhost]find . -type d -exec chmod u=rwx,g=rx,o= '{}' \;
[root@localhost]find . -type f -exec chmod u=rw,g=r,o= '{}' \;

And then in the "for" loop the same thing would apply:

find $d -type d -exec chmod ug=rwx,o= '{}' \;
find $d -type f -exec chmod ug=rw,o= '{}' \;

According to the find manual, "[...] the braces are enclosed in single quote marks to protect them from interpretation as shell script punctuation."

Yes, it should be quoted with single quotes. Thank you, the commands were corrected

Definitely wrote that down in my notes. Thanks for the tip.

Thank you for that script so much!!! It solved my issues of transferring a site back and forth from a test server to a production server!

Hi guys,

if ever there is a way to recover the apache user automatically:

apache_group=$(ps axho user,comm|grep -E "httpd|apache"|uniq|grep -v "root"|awk 'END {if ($1) print $1}')

I have tested this in Ubuntu 11.04 and CentOS 6 and it works perfectly.

I've gone through this entire thread and am still lost. Is there a comprehensive guide to file / directory permissions? I work almost exclusively on shared hosting packages, and just need a guide to tell me:

Drupal core directories and files: I have everything set to 755. Good? Bad?
everything in sites/* is set to 755 except default.settings.php and settings.php. I tried to set them to 444 but they revert to 644 every time.

Using FileZilla to set the permissions, btw.

I have checked index.php and it is at 644, as is .htaccess.

The site is currently in a subdomain named 'beta' which points to a folder also called 'beta' inside the root. Both the beta directory and the root directory (public_html) are set to 750.

This is for a Drupal 7 site. Did anything change from 6 to 7?

I am using Drupal Security Review and it is telling me that every single directory and file is writable, including the settings.php file.

I would greatly appreciate it if someone could point me to a handbook, a page, something that explains it step by step, because it is not clear for me.

Thank you in advance!

PS QUICK UPDATE I tried to set the file permissions for the files directory and all subdirectories and files as proposed in the original post (rwxrws--- for directories and rw-rw---- for files within those directories, and none of my images displayed, except for a views slideshow on the front page which still seemed to be displaying. -- rek

There is a section called Summarizing the permissions on this document that explains the permissions you must have for a secure installation. There is a session for Sysadmin explaining about hosted installations. Read carefully the disclaimer too.

When I implement these permissions on a VPS running a single site, it causes the GUI-based "install new module" capability of D7 to fail. Now, I understand that installing modules with drush may be "better", but I don't think my users will agree. I can restore that functionality by making www-data own everything, but seems to defeat the purpose of all of this. Is there a solution that enables gui-based module installation that leaves as much of the security intact as possible? Changing just permissions or ownership in sites/all doesn't do the trick. If I'm just running a single site with few users (all admin), do I need these permission changes at all?

Yes, you need write permission to the .../sites/all/modules directory to use the GUI based install. Then the module will be owned by your web server. You can either give write access to the group (and place the web server in the group owning the directory) or change the owner to the web server's id.

Both ways have security implications as noted above; On my servers I change the directory owner temporarily to the web server id (via SSH), load the modules then change the ownership of the directory and uploaded files/directories to the ftp upload id.

Jerry Stuckle

It seems that as well as write permission to .... sites/all/modules, you also need the .../sites/default directory to be owned by the web server's id for the GUI installer to work. Write permission to .../sites/default is not sufficient. (7.15)

Taking the handbook script as a base, I cooked the following script a couple of weeks ago for a friend of mine, been using it myself with much bliss :)

#!/bin/bash
trim () { read -rd '' $1 <<< "${!1}"; }
a=$1; trim a;  drupal_path=$(readlink -e  -- "$a" 2>&-)
b=$2; trim b; apache_group=$(id       -ng -- "$b" 2>&-)
c=$3; trim c;  drupal_user=$(id       -nu -- "$c" 2>&-)
errors=''
if [[ "$#" != "3" ]]; then
  echo "ERROR: wrong parameter count." >&2
  errors='X'
else
  if [[ "$drupal_path" == "" ]]; then
    if [[ "$a" == "" ]]; then
  echo "ERROR: you must provide a non-blank path."
    else
  echo "ERROR: \`$a' is not a valid path." >&2
    fi
    errors='X'
  elif [[ ! -d "$drupal_path/sites" || ! -f "$drupal_path/modules/system/system.module" ]]; then
    echo "ERROR: \`$a' is not a valid drupal installation." >&2
    errors='X'
  fi
  if [[ "$apache_group" == "" ]]; then
    if [[ "$b" == "" ]]; then
  echo "ERROR: you must provide a valid group name."
    else
  echo "ERROR: \`$c' is not a valid group." >&2
    fi
    errors='X'
  fi
  if [[ "$drupal_user" == "" ]]; then
    if [[ "$c" == "" ]]; then
  echo "ERROR: you must provide a non-blank user name."
    else
  echo "ERROR: \`$b' is not a valid user." >&2
    fi
    errors='X'
  fi
fi
if [[ "$errors" == "X" ]]; then
  echo >&2
  echo "This script is used to fix permissions of a drupal installation." >&2
  echo >&2
  echo "Usage:" >&2
  echo "  (sudo) ${0##*/} <drupal_path> <apache_group> <drupal_user>" >&2
  echo >&2
  echo "   <drupal_path>  path to your drupal installation (eg. \`/var/www/drupal')" >&2
  echo "   <drupal_user>  username of the user who "owns" this drupal installation (eg. \`you')" >&2
  echo "                    if no <drupal_user> is specified, the currently running user is used" >&2
  echo "  <apache_group>  group under which apache runs (eg. \`www-data')" >&2
  echo >&2
  echo "This script performs the following steps:" >&2
  echo >&2
  echo "  1. For every file NOT belonging to the given user and group, change its ownership to them," >&2
  echo "  2. Change the permissions of every non-conformant directory to \`rwxr-s---' (ie. u=rwx,g=srx,o=; or 02750)," >&2
  echo "  3. Change the permissions of every non-conformant file      to \`rw-r-----' (ie. u=rw,g=r,o=; or 0640)," >&2
  echo "  4. For every site in the \`sites' directory:" >&2
  echo "     4.1. If there's a \`files' directory:" >&2
  echo "          4.1.1. Change the permissions of every non-conformant directory in \`files' to \`rwxrws---' (ie. u=rwx,g=srwx,o=; or 02770)," >&2
  echo "          4.1.2. Change the permissions of every non-conformant file      in \`files' to \`rw-rw----' (ie. u=rw,g=rw,o=; or 0660)." >&2
  echo "     4.2. If there's a \`bin' directory:" >&2
  echo "          4.2.1. Change the permissions of every non-conformant directory in \`bin' to \`rwxrws---' (ie. u=rwx,g=srwx,o=; or 02770)," >&2
  echo "          4.2.2. Change the permissions of every non-conformant file      in \`bin' to \`r-xr-x---' (ie. u=rx,g=rx,o=; or 0550)." >&2
  echo >&2
  echo >&2
  exit 1
fi
echo -n "Changing ownership of all contents to \`$drupal_user:$apache_group'..."
find -O3 $drupal_path \( -not -group "$apache_group" -o -not -user "$drupal_user" \) -exec chown "$drupal_user":"$apache_group" '{}' +
echo " done!"
echo
echo -n "Changing the permissions of all non-\`file', non-\`bin' directories in \`$drupal_path' to \`rwxr-s---'..."
find -O3 $drupal_path -iregex "'$drupal_path/sites/[^/]*/files'" -prune -o -iregex "'$drupal_path/sites/[^/]*/bin'" -prune -o -type d -not -perm u=rwx,g=srx,o= -exec chmod u=rwx,g=srx,o= '{}' +
echo " done!"
echo -n "Changing the permissions of all non-\`file', non-\`bin' files       in \`$drupal_path' to \`rw-r-----'..."
find -O3 $drupal_path -iregex "'$drupal_path/sites/[^/]*/files'" -prune -o -iregex "'$drupal_path/sites/[^/]*/bin'" -prune -o -type f -not -perm u=rw,g=r,o= -exec chmod u=rw,g=r,o= '{}' +
echo " done!"
echo
echo "Scanning sites..."
for site in $drupal_path/sites/*; do
  if [[ -d "$site" ]]; then
    echo "  Site \`${site##*/}':"
    if [[ ! -d "$site/files" ]]; then
      echo "    No \`files' directory found."
    else
      echo "    Processing \`files' directory."
      echo -n "      Changing the permissions of all \`files' directories in \`$site' to \`rwxrws---'..."
      find -O3 $site/files -type d -not -perm u=rwx,g=srwx,o= -exec chmod u=rwx,g=srwx,o= '{}' +
      echo " done!"
      echo -n "      Changing the permissions of all \`files' files       in \`$site' to \`rw-rw----'..."
      find -O3 $site/files -type f -not -perm ug=rw,o= -exec chmod ug=rw,o= '{}' +
      echo " done!"
    fi
    if [[ ! -d "$site/bin" ]]; then
      echo "    No \`bin'   directory found."
    else
      echo "    Processing \`bin'   directory."
      echo -n "      Changing the permissions of all \`bin'   directories in \`$site' to \`rwxrws---'..."
      find -O3 $site/bin -type d -not -perm u=rwx,g=srwx,o= -exec chmod u=rwx,g=srwx,o= '{}' +
      echo " done!"
      echo -n "      Changing the permissions of all \`bin'   files       in \`$site' to \`r-xr-x---'..."
      find -O3 $site/bin -type f -not -perm ug=rx,o= -exec chmod ug=rx,o= '{}' +
      echo " done!"
    fi
  fi
done
echo "done!"
exit

Mind you, I've not tested this extensively, but haven't run into any problems so far... I find it's pretty useful as a "one shot" solution.

I sort of added to the directory structure by creating a "bin" directory under sites/<whatever>/bin, and giving execute permissions to that directory alone (an example of a module needing executables is Printer, email and PDF versions, with the wkhtmltopdf backend), if the executable files are needed somewhere else (as is the case for the above module, which needs them at sites/<whatever>/libraries) you can always soft-link (ln -s) them.

What do you think of it? Am I missing something? Could something be improved?

PS: my bash coding could use some (constructive) criticism :)

I'd like to run this script, but have a ton of files in my sites/default/files folder with proper permissions, on s3fs, so I'd rather not touch those (I've tried running the script as is, and it just seems to take forever... eventually killed it).

Anyone with more knowledge know how to modify fix-permissions.sh so that it leaves the sites/default/files directory alone?

Thanks in advance!

I actually have the same question. I hope it gets answered.

Below is a bash script, name it whatever you want and make it executable, this seems easier than the proposal above....KISS!

I am open to any suggestions or changes because I use this script for every site I create.

!/bin/bash
# Script made by David Rush, davidprush@gmail.com
# Changes the permissions of the Drupal sites directory to secure after isntallation
# Very Important Put this script in the same directory as your site/ directory
# You are free to distribute, change, delete, or whatever the hell you want with this script
echo "Start fixing permissions..."
# Declare some DIR paths
FILES="sites/default/files"
SETTINGS="sites/default/settings.php"
SITES="sites"
DEFAULT="sites/default"
THEMES="sites/all/themes"
MODULES="sites/all/modules"
DSETTINGS="sites/default/default.settings.php"
# Paths for binary files
TAR="/bin/tar"
RM="/bin/rm"
CHOWN="/bin/chown"
FIND="/bin/find"
CHMOD="/bin/chmod"
# Change permissions to be secure
$CHOWN -R apache:apache sites
$CHMOD 775 ${SITES}
$CHMOD 775 ${FILES}
$CHMOD g+w -R ${FILES}
$CHMOD 440 ${SETTINGS}
$CHMOD 440 ${DSETTINGS}
$CHMOD 755 ${DEFAULT}
$CHMOD 755 -R ${THEMES}
$CHMOD 755 -R ${MODULES}
echo "Done fixing permissions, verify all permisssions are correnct!"
# script done

D.P. Rush
www.millisting.com

Using Drupal Everyday!

David, your script worked! Thanks!

As beginner, I have read comments and shouldn't the owner be other user than apache?

Something like:
$CHOWN -R greg:apache sites

shouldn't the owner be other user than apache?

Actually, almost everything from above is about insecure/vulnerable setups and as you just noticed - the owner should never ever be an apache process since the foremost rule of web server security is to forbid the web server to write where it shouldn't write. This is the basics of web server configuration which doesn't depend on used CMS and I wonder how so many people advice all these flawed techniques.

If you are a beginner, then I encourage you to stand on the right path when securing filesystem of your web applications.
My first comment here was about exactly this - about using Linux ACLs: http://drupal.org/node/244924#comment-5179950
It is a bit outdated and currently I'm using a set of scripts, but the idea is the same.

P.S. /me is thinking about providing his scripts to the community.

So this is done. I created a child page where I documented my script: http://drupal.org/node/1825824

Just wanted to let people know that there's discussion on a drush command to fix permissions at http://drupal.org/node/990812

There's also information on fixing permissions/owner after drush dl at http://drupal.org/node/841310

i followed these instructions to a t (at least I think I did) but now the themes for the sites that are under the sites folder (i.e., the ones that got moved to home and a symlink) wont work -- the error log shows that it can't find various css files, even though they do exist at the path they are supposed to be in. I tried removing the theme, and reinstalling it, but same problem .

I just had an issue with the index.php settings on a CentOS Server when doing a drupal 7 upgrade through drush, so hoping that this might help others. I was following the instructions above which said the index.php file should be:
-rw-r-----  1 greg-user   www-data     873 2007-11-13 15:35 index.php

I replaced "www-data" with "nobody" since I thought that is the CentOS equivalent to Ubuntu's "www-data" which gives permission to the web server (apache). But I was unable to access the site after this. Turns out I had to set do the following:

chgrp greg-user index.php

and that did the trick. Replace "greg-user" with whatever the Linux username is on your server for that particular site.

Anyway, hope that helps someone who has a similar problem.

CentOs doesn't have www-data and I am using root as my user while testing.

Cannot follow above directions, they are way too confusing.

I want to know what my drupal folder should be set to for ownership and permissions and down the line.

I have a subdomain where drupal resides.

At the moment I can't update modules not to mention other problems because nothing is set correctly.

Everything seems to be 6226 and then I read how that happened. Is everything 6226 supposed to be "apache"?

I am NOT on UBUNTU but CentOs vps so it is different and I'm completely lost on this most important set up.

I have read all of the above and it is not clear to me and I don't want to run a bash script.

Hope someone can help.

Centos is a distro of Red Hat

You are correct in that there is no group 'www-data' on Centos.

As Far as I know the Centos equivalent of 'www-data' is 'apache' so for example the shell line to change ownership of all files in and below a directory would be chown -R user:apache .
(replacing user with your dev or sysadmin username.)

I hope that helps

Tim

Hi
has anybody written a scirpt specifically for php-fpm ?
tx
Simone

A reasonably effective method of ensuring file ownership after git pulls is to use git hooks.

If the following script is run on checkout and merge the entire drupal installation should use the correct ownership. This script will need to be extended for multisite installs to cover sites/*/files etc

#!/bin/bash
owner='root'
group='www-data'
cd ${GIT_DIR}/../
echo "Fixing user:group ownership ..."
chown --preserve-root ${owner}:${group} .
shopt -s dotglob
chown -R --preserve-root ${owner}:${group} *
shopt -u dotglob
echo "Fixing file/directory permissions in `pwd` ..."
find . -not -path "./.git*" -not -path "./sites/default/files*" -type d -exec chmod u=rwx,g=rx,o= '{}' \;
find . -not -path "./.git*" -not -path "./sites/default/files*" -type f -exec chmod u=rw,g=r,o= '{}' \;
cd ${GIT_DIR}/../sites
echo "Fixing file/directory permissions in `pwd` ..."
chmod ug=rwx,o= ./default/files
find ./default/files -type d -exec chmod ug=rwx,o= '{}' \;
find ./default/files -type f -exec chmod ug=rw,o= '{}' \;
exit 0

This is only on dedicated server???

No, you can do it with a FTP-Client like FileZilla too. You need much patience but it works and you should definetly do it!

I took the script above and put it up on Github (and enhanced a bit and fixed some minor issues). Pull requests welcome.

https://github.com/MinnPost/drupal-permissions

--
zzolo

The beginning of the script contains the unused and highly misleading section

# Script arguments
drupal_path=${1%/}
drupal_user=${2}
httpd_group="${3:-www-data}"

You cannot, in fact, pass arguments that way. You need to use --drupal-user etc. I initially thought this was a problem with @zzolo's version of the script, when in fact the problem exists in the original (sorry, zzolo!)

Nice one. Just sent you a pull request.

Can you explain why you chose to add further tweaks for .git, .gitignore and the various root level .txt files? I'm curious.

For people running their own servers, I find setting a system wide UMASK of 027 simplies a lot of this. You could then chgrp your docroot (ie: public_html) to the shared group (www-data) and then setgid on it with chmod g+s. This would insure any new folders or files created would have 750 and 740 permissions respectively, and allow the web server access to the files. Then you just manually create your public files folder (and settings.php) and give the server permission to write to it with chmod g+w.

The only thing left to deal with is managing permissions on newly created folders and files WITHIN the files folder. In my testing, everything created within the files folder is owned by the server, and has 775 and 664 respectively, of course being a public space this shouldn't be too much of an issue.

Of course doing this on a brand new system may be easier than implementing on an existing system.

The only thing left to deal with is managing permissions on newly created folders and files WITHIN the files folder.

Drupal calls drupal_chmod() after new directory is created. There is 'file_chmod_directory' setting which can be set in settings.php or with variable_set().
To make Drupal set 2775 permissions for new folders you can add this line to settings.php:

$conf['file_chmod_directory'] = octdec('2775'); // 1533

By default it is set to 0775. There is similar file_chmod_file for files (0644).

Of course you should additionaly configure your OS:

  1. Add user to www-data group (see more here)
  2. Set web-server umask (like this). In my case umask is 002. It is needed for recursively created folders.
  3. Set proper group and owner (www-data:kalabro) to already created files and folders.

After QUITE a bit of trial and error (I have 13 unused 'test' image styles in my drupal install now) I finally found instructions that WORK! Kalabro, thanks for setting us all straight here. Setting umask to 002 is a critical component here, and my site didn't created directories with 775 until I set that.

I too was having the issue where an image style would be created, with apache:apache ownership and 755 permissions. This worked fine until I did 'chown web:apache -R' to the file directory (to allow 'web' to edit files via sftp) and suddenly the owner was 'web' and not 'apache' ... Now I can re-apply the 'correct' ownership (web:apache) WITHOUT breaking image styles, since perms are set to 775, and both user 'web' and group 'apache' have write permissions.

Thanks @kalabro!! I only wish your comments weren't hidden at the bottom of a thread, but somehow front/center in drupal's documentation on file permissions and/or setting up image styles.

Member of the PowerPot Team thepowerpot.com

@swensor, I am glad my research helped someone. I'm sure that the whole “Securing file permissions and ownership” page needs rewriting and reorganizing. It's very time-expensive task, but I should try to start.

After setting sites/default/files/ to the the ownership permissions recommended here in this thread in this user:group style:

drwxrwx--- 7 www-data greg-group 4096 2008-01-18 11:02 files/

the styles directory installed during drupal install within the files directory has greg-user:www-data style ownership:

drwxrwx--- 3 greg-group www-data 4096 Jan 28 00:08 styles

so when a user adds an image field to a basic page and then creates a page from that the content created in that process in the stles directory ends up with www-data:www-data permissions and the user cannot modify those files via SFTP or filesystem cli.

drwxrwxr-x 3 www-data www-data 4096 Jan 28 00:08 public

What is the correct step by step permissions commands to secure a new install that covers this issue?

Please don't say add the user to the www-data group. If that's correct then please say that! :)

I got significantly better performance by changing all:
find [...] -exec chmod [...] \;
to:
find [...] -exec chmod [...] \+
(~3 secs compared to ~2 mins)

Changing the ; (at the end of the exec action) to a + means matched files are appended onto the end of the one chmod command (as opposed to running chmod on each file individually).

Example: (echo simply displays the command, rather than actually running it)

$ find . -type f -exec echo chmod u=rw,g=r,o= '{}' \;
chmod u=rw,g=r,o= ./file1.txt
chmod u=rw,g=r,o= ./file2.txt
chmod u=rw,g=r,o= ./file3.txt

$ find . -type f -exec echo chmod u=rw,g=r,o= '{}' \+
chmod u=rw,g=r,o= ./file1.txt ./file2.txt ./file3.txt

See man find for more details.

If someone else can confirm this works and doesn't have any adverse side-effects, can we edit the script above?

From what I can gather, one of the places that people locate tmp directories is in the root directory of Drupal. I don't know if this is the proper place for it, but if it is located in the Drupal root directory, the permissions for this directory need to be different than most directories which are set to drwxr-x---.

I have set mine to drwxrwx--- and this seems to avoid writing errors.

I'm interested in the thoughts of others.

Thanks

Ooops, didn't mean to reply to this comment. Apologies.

This script is great but any files in version control will be marked as changed if their permissions change. As a result, I prefer to separate out my scripts for handling tmp, public & private files (none of which I keep in version control) so that I can run them on a remote site without having to worry about what's happening to Git status etc. I've shared my work so far on GitHub (along with some other scripts): https://github.com/danbohea/drupal_remote_scripts

Hello,

as I am currently building a site with Drupal 7.26 and had issues with permissions on Apache 2 on Ubuntu 12.04, I tried to get all the info from this thread and make a solution for my sites. I ended up with a script, which I want to share now.

Note (Warning/Disclaimer):
I am a Linux and Ubuntu beginner and this is my first edit of a bash script, so I can't guarantee it is correct. Please comment, if something didn't work for you or could be done better.

1) I started here:
https://drupal.org/node/244924
And among all other things, I have found this summary usefull:
https://drupal.org/comment/4519336#comment-4519336

2) As a base, I have taken a script from:
https://github.com/MinnPost/drupal-permissions/blob/master/drupal-permis...
I call it "original script"

I didn't commit my script back to github. When the script proves to be correct, somebody experienced is welcome to update it.

3) Performance fix according to:
https://drupal.org/comment/8429975#comment-8429975
http://ss64.com/bash/find.html

a) Changing find chmods like:
find ${x} -type d -exec chmod ug=rwx,o= '{}' \;
to:
find ${x} -type d -exec chmod ug=rwx,o= '{}' \+

original "find ${x} -type d -exec chmod ug=rwx,o= '{}' \;" will call separate chmod for each found item (directory, ev. file), this could make performance issues.

Proposed "find ${x} -type d -exec chmod ug=rwx,o= '{}' \+" will first build a list of all found items (directories, ev. folders) '{}' and then perform chmod just once, this is faster.

b) Combining multiple files to one chmod like:
chmod u=rwx,go= CHANGELOG.txt
chmod u=rwx,go= COPYRIGHT.txt
to:
chmod u=rwx,go= CHANGELOG.txt COPYRIGHT.txt

These performance fixes, when run on your (busy) live site, will minimize possible errors to your online users. During running the script (original or proposed), first all directories are set non writable (by server) u=rwx,g=rx,o= and all files are set non writable (by server) u=rw,g=r,o=. Then script (original or proposed) continues and makes sites/*/files writable. If there are performance issues (original script), i.e. making files writable takes too long or is performed too late, your online users could get errors e.g. when uploading images to your site. With proposed performance fix, this risk is reduced.

4) Added chmod for sites/*/settings.php and sites/*/default.settings.php:
See: https://drupal.org/comment/4519336#comment-4519336

5) Added chmod for sites/*/files/.htaccess
It was recommended to have .htaccess in sites/*/files directories without server write access, see:
https://drupal.org/comment/4519336#comment-4519336

To disable executing php scripts from files directory:
https://drupal.org/comment/4133742#comment-4133742 - Suggestion adding: AddHandler default-handler php
https://drupal.org/comment/4572986#comment-4572986 - Apache config suggestion

But in Drupal 7.26 I have found section starting with:
# Set the catch-all handler to prevent scripts from being executed.
so I think, this is already handled with default Drupal installation?
And I do not need to edit .htaccess, just to chmod it?
Let us know, if you think otherwise.

NOTE 1:
Every server, especially shared hosting can be different, so if some set of permissions work on one hosting, it might not work on another. I have experienced this problem some time ago, because some hidden load balancer was used by my shared hosting provider. See e.g.:
https://drupal.org/comment/3026118#comment-3026118
https://drupal.org/comment/4080864#comment-4080864
https://drupal.org/comment/4519336#comment-4519336 - general permissions overview with proposed alternatives
(Adjust the permissions in the script accordingly, if the script permissions do not work for your server.)

NOTE 2:
I would rather prefer solution based on Linux ACL proposed at:
https://drupal.org/comment/5179950#comment-5179950
but it didn't fit my needs "out-of-the-box" and it is currently beyond my abilities and time restrictions to learn and adapt the ACL solution.

NOTE 3:
Personally I enjoyed the motto from user "suffering drupal" (https://drupal.org/user/215292):
"I started with Drupal in 2007 and then my life got stuck..."
:)
from: https://drupal.org/comment/4714254#comment-4714254

The script is uploaded here:
http://pastebin.com/N2w1EHs8

removed