使用.htaccess文件禁用一个目录中的所有CGI(php、perl等)。

19

我有一个目录,用户可以在其中上传文件。

为了避免安全问题(例如某人上传恶意php脚本),我目前通过添加 .data 来更改文件的扩展名,但是当下载文件时,用户必须手动删除 .data

另一个常见的解决方案是将文件上传到不被Apache服务的目录中,并通过调用 readfile() 的方式,让一个php脚本管理所有下载

我想做的是简单地禁止在上传文件夹中执行任何脚本(php、perl、cgi脚本或者未来可能安装的任何其他脚本)。这个SO答案建议在该文件夹中的.htaccess文件中添加以下行:

SetHandler default-handler

但在我的情况下,这没有任何影响(我放在那个文件夹中的示例PHP脚本仍然被执行)。我做错了什么?

Apache配置

这台机器是一台VPS(虚拟专用服务器),运行着Debian GNU/Linux 6.0.7 (squeeze),就我所记得的(我记录了我在该服务器上运行的所有命令,所以我的“记忆”应该相当准确),我除了运行sudo apt-get install php5和创建文件/etc/apache2/sites-enabled/mysite.com外,没有修改过apache2的配置,该文件的内容如下:

<VirtualHost *:80>
  ServerAdmin webmaster@localhost
  ServerName  mysite.com
  ServerAlias www.mysite.com

  DocumentRoot /home/me/www/mysite.com/www/
  <Directory />
    Options FollowSymLinks
    AllowOverride All 
  </Directory>
  <Directory /home/me/www/mysite.com/www/>
    Options Indexes FollowSymLinks MultiViews
    AllowOverride All 
    Order allow,deny
    allow from All
  </Directory>

  ErrorLog ${APACHE_LOG_DIR}/error.log

  # Possible values include: debug, info, notice, warn, error, crit,
  # alert, emerg.
  LogLevel warn

  CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>

@anubhava 谢谢你的回复。在 /etc/apache2/sites-enabled/mysite.com 中,我的 DocumentRoot 设置为 /home/me/www,文件 /home/me/www/subdir/.htaccess 包含一行 SetHandler default-handler,并可由 www-data 读取(我使用 sudo su www-data; whoami; cat /home/me/www/subdir/.htaccess 进行了检查),所以我怀疑问题与 AllowOverride 或类似的内容有关。 - Suzanne Soy
1
请确保启用了 .htaccess 文件,方法是在其中放置一些垃圾文本并查看是否会生成 500 错误。 - anubhava
我尝试了各种选项(直接在/etc/apache2/sites-enabled/mysite.com中),发现唯一能禁用PHP脚本的方法是放置<Directory /home/me/www/subdir/> php_flag engine off </ Directory>,甚至不需要使用 RemoveHandler .php .php3 .php4 .php5 .phtml。这意味着对我来说,PHP似乎会绕过SetHandler指令。不幸的是,这意味着如果有一天我安装了另一个CGI引擎,例如perl,那么我也需要记得将其禁用,因此使用php_flag engine off绝对不是一种可靠的禁用文件夹内脚本执行的方法。 - Suzanne Soy
是的,我同意SetHandler绝对是更强大的解决方法。我尝试过了,它对我有效,不确定为什么它对你无效。 - anubhava
@anubhava 是的,我也试过了,还尝试将两行内容放在/etc/apache2/sites-enabled/mysite.com中的<Directory>指令里,但是还是没有用。最终我找到了解决方案:指令被应用于目录本身(这就是为什么当访问该目录时出现404错误),而不是其内部的文件,所以我必须写上<Files *>SetHandler default-handler</Files>,在这种情况下指令会递归地应用于文件夹本身、其中的文件和任何子文件夹。感谢所有的帮助! - Suzanne Soy
显示剩余4条评论
2个回答

34

把这段代码放到你的.htaccess文件中:

<Files *>
    # @mivk mentionned in the comments that this may break
    # directory indexes generated by Options +Indexes.
    SetHandler default-handler
</Files>

但是这里存在一些安全漏洞:有人可以在子目录中上传一个 .htaccess 文件,覆盖这些设置,他们也可能覆盖 .htaccess 文件本身!

如果您担心该选项的行为将来发生变化,请将此代码放入您的 /etc/apache2/sites-enabled/mysite.com 文件中。

    <Directory /home/me/www/upload/>
            # Important for security, prevents someone from
            # uploading a malicious .htaccess
            AllowOverride None

            SetHandler none
            SetHandler default-handler

            Options -ExecCGI
            php_flag engine off
            RemoveHandler .cgi .php .php3 .php4 .php5 .phtml .pl .py .pyc .pyo
            <Files *>
                    AllowOverride None

                    SetHandler none
                    SetHandler default-handler

                    Options -ExecCGI
                    php_flag engine off
                    RemoveHandler .cgi .php .php3 .php4 .php5 .phtml .pl .py .pyc .pyo
            </Files>
    </Directory>

如果无法修改Apache配置,则将文件放在具有以下目录结构的.htaccess中:
/home/me/www/
          |- myuploadscript.php
          |- protected/
              |- .htaccess
              |- upload/
                  |- Uploaded files go here

这样一来,由于上传文件保存在.../protected的子目录中,而不是直接在protected目录下,因此没有人应该能够覆盖您的.../protected/.htaccess文件。

根据我所知,您应该是比较安全的。


好主意。更简单的解决方案是将.htaccess文件设置为所有人只读。当然,这在某些情况下可能不起作用,具体取决于谁以及如何编辑.htaccess文件。 - Tillebeck
4
虽然在文件上执行 chmod 444 命令可以将其设置为只读,从而防止直接修改该文件,但它无法防止替换该文件(删除并创建一个新文件),因为此操作涉及的是目录的权限而非文件本身的权限,因此如果你不希望文件内容被更改,我不建议依赖只读文件。 - Suzanne Soy
1
使用“SetHandler default-handler”在某些情况下会破坏目录索引(“Options +Indexes”)。我花了一段时间才缩小到这一点,所以如果有人遇到同样的问题,我会提到它。我一直收到404错误,并且日志只显示“[error] [client ...] Attempt to serve directory: /...”。 - mivk

2
我的Godaddy设置不允许我编辑httpd.conf文件,而且由于他们为我实现了php,所以php_flag命令无法使用。
我能在我的.htaccess文件中使用以下内容:
SetHandler default-handler
AddType text/plain php

我将这个文件放在FTP用户允许访问的目录上面,这会强制该目录中以及所有子目录中的PHP文件显示为纯文本。

这对于其他文件类型也适用。您只需要添加另一行,指定您想要强制显示为纯文本的文件扩展名。例如,AddType text/plain cgi


1
请注意保护您的.htaccess文件,以防止上传名为.htaccess的文件并覆盖您的文件。请参见我的答案结尾,了解如何在没有访问httpd.conf的情况下完成此操作。 - Suzanne Soy
1
似乎我忽略了你帖子的结尾。直到我自己实现它之前,我不认为我完全理解了它。你的建议正是我正在做的事情。htaccess在“/protected”中,而FTP用户只能访问“/protected/user”。我想我应该测试一下,在用户子目录中添加一个额外的htaccess文件是否会覆盖顶层的限制。我认为它不会...但我并不知道所有的东西,而且过度假设以前曾经让我陷入麻烦。 - Taylor
我认为 AllowOverride None 应该可以解决这个问题,但我不能百分之百确定它是否无法被任何方式覆盖。不过你说得对,一个经过加固的设置需要仔细思考并双重检查这种故障点。 - Suzanne Soy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接