Problem/Motivation

After following the guidelines on the Securing file permissions and ownership documentation page, and running this module, the report says "PHP files in the Drupal files directory can be executed.".

On the "Executable PHP" page it says:

The Drupal files directory is for user-uploaded files and by default provides some protection against a malicious user executing arbitrary PHP code against your site. Read more about the risk of PHP code execution on Drupal.org.

The .htaccess file is writable which poses a risk should a malicious user find a way to execute PHP code they could alter the .htaccess file to allow further PHP code execution.

From /admin/reports/security-review/help/security_review/executable_php.

.htaccess is 660:

$ ls -al web/sites/default/files | grep .htacces
-rw-rw----   1 user      www-data     486 12 feb  2025 .htaccess

Setting it to read-only seems to satisfy Security Review:

$ chmod 440 web/sites/default/files/.htaccess
$ ls -al web/sites/default/files | grep .htacces
-r--r-----   1 user      www-data     486 12 feb  2025 .htaccess

The report goes green and says:

"PHP files in the Drupal files directory cannot be executed."

The permission is probably set by the last line, from the Securing file permissions and ownership doc page:

$ cd /var/www/example.org/public_html/web/sites
$ find . -type d -name files -exec chmod ug=rwx,o= '{}' \;
$ find ./*/files -type d -exec chmod ug=rwx,o= '{}' \;
$ find ./*/files -type f -exec chmod ug=rw,o= '{}' \;

Is that correct, or should we recommend another command?

Steps to reproduce

Proposed resolution

  1. Check if the documentation is correct:
    ✅ Yes, it is correct.

    For Drupal to protect against PHP file execution in the Drupal files directory then a .htaccess file needs to exist with the following content and not be writeable by the webserver.

    From Preventing execution of untrusted PHP.

  2. Check if the recommended steps to secure folders and files is correct:
    ✅ Command to make .htaccess read-only could be added, and it has been, as seen below.

    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:

    $ cd /var/www/example.org/public_html/web/sites
    $ find . -type d -name files -exec chmod ug=rwx,o= '{}' \;
    $ find ./*/files -type d -exec chmod ug=rwx,o= '{}' \;
    $ find ./*/files -type f -exec chmod ug=rw,o= '{}' \;
    $ find ./*/files/.htaccess -type f -exec chmod ug=r,o= '{}' \;
    

    From Securing file permissions and ownership.

  3. Verify if the check in the code is correct:
    ✅ It works well, and as intended.
    if ($writable_htaccess) {
      $findings[] = 'writable_htaccess';
      if ($result !== CheckResult::FAIL) {
        $result = CheckResult::WARN;
      }
    }
    

    From https://git.drupalcode.org/project/security_review/-/blob/3.1.x/src/Plug....

  4. We could include "... and Securing file permissions and ownership" in the src/Plugin/SecurityCheck/ExecutablePhp.php message, to give the users a solution?
    ✅ The MR has been committed.

Remaining tasks

User interface changes

API changes

Data model changes

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

ressa created an issue. See original summary.

ressa’s picture

Issue summary: View changes
smustgrave’s picture

Not sure I follow what would the other command be?

smustgrave’s picture

Actually maybe a bug?

       if ($writable_htaccess) {
          $findings[] = 'writable_htaccess';
          if ($result !== CheckResult::FAIL) {
            $result = CheckResult::WARN;
          }
        }

This actually is rendering as a failure

ressa’s picture

Issue summary: View changes

Thanks for taking a closer look at this, it seems important.

I found another documentation page Preventing execution of untrusted PHP which states that the .htaccess file in the files folder needs to not be writeable by the webserver, and added it in the Issue Summary.

smustgrave’s picture

So if it were a warning that would make sense?

ressa’s picture

I am not sure ... I think the first step is to check if the documentation is correct (step#1 under "Proposed resolution") and if it is, then proceed to step #2, since the conclusion from step #1 will dictate if the commands are correct, and dictate what should be checked in step #3.

greggles’s picture

It is valid to say that the .htaccess should not be writable by the webserver.

ressa’s picture

Great, thanks for verifying @greggles, then the next step #2 is checking the commands on Securing file permissions and ownership, specifically this line, which sets .htaccess to rw:

$ find ./*/files -type f -exec chmod ug=rw,o= '{}' \;

Assuming that is correct, should we add an extra command, dedicated to .htaccess, like this?

$ cd /var/www/example.org/public_html/web/sites
$ find . -type d -name files -exec chmod ug=rwx,o= '{}' \;
$ find ./*/files -type d -exec chmod ug=rwx,o= '{}' \;
$ find ./*/files -type f -exec chmod ug=rw,o= '{}' \;
$ find ./*/files/.htaccess -type f -exec chmod ug=r,o= '{}' \;

PS. I also wonder if we should include "... and Securing file permissions and ownership" in the src/Plugin/SecurityCheck/ExecutablePhp.php message, to give the users a solution?

The Drupal files directory is for user-uploaded files and by default provides some protection against a malicious user executing arbitrary PHP code against your site. Read more about the risk of PHP code execution on Drupal.org and Securing file permissions and ownership.

greggles’s picture

That all seems like a good idea.

Note that making the file read only is good, but unless the owner is changed away from the webserver user the webserver could always change the file to be writable.

ressa’s picture

Title: .htaccess file is writable which poses a risk -- false positive? » .htaccess file is writable, using the current Securing file permissions and ownership doc page
Issue summary: View changes
Status: Active » Needs review

Perfect, I added the .htaccess on Securing file permissions and ownership as it was suggested here, and added you caveat about webserver and potential scenario of changing the file to be writeable. I created an MR which adds the link to the file, like I suggested.

smustgrave’s picture

@greggles thoughts? I'm fine with it

smustgrave’s picture

Category: Support request » Task
greggles’s picture

Yes, seems great!

smustgrave’s picture

Status: Needs review » Fixed

Rock on

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

ressa’s picture

Issue summary: View changes

Nice, thank you both for maintaining this great module!

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.