安全指引


引言


要想进一步了解文件上传服务所存在的潜在安全风险及相应的预防措施,请参阅 OWASP 的文档 - 排解文件上传的限制因素(Unrestricted File Upload)

关于如何高安全性地安装本插件的文件上传服务,请参阅本文段落中的示例 - 文件上传服务的安全配置

对于如何预防图像处理库中的潜在漏洞,请参阅 - 图像处理安全配置

默认情况下,所有上传处理示例只允许上传图像文件,这样可以预防部分攻击途经,但不应将其视为唯一的保护措施。

还请查看本文档中的漏洞列表,它主要与服务端处理示例及其配置方式有关。

项目目的


请注意,本项目并不是一个完整的文件管理产品,而主要是一个 jQuery 的客户端文件上传插件库。

资源包中的服务端处理示例,它们只是演示客户端文件上传功能的示例。

有一点需要特别强调,默认情况下不做用户身份验证

  • 任何人都可以上传文件
  • 任何人都可以删除已上传的文件

在某些场景下这也许是可以被接受的,但对于大多数项目,你可能会希望通过扩展上传处理示例来整合用户验证模块。当然,你也可以重写一个自己的上传处理程序。

你还可以配置你的 web 服务器来提供一个安全的文件上传服务,例如,参考本文的文件上传服务的安全配置

文件上传相关风险规避


防止服务端远程代码执行

为了预防在服务端非法执行脚本或二进制文件,上传目录(例如,PHP 处理程序的默认目录为 server/php/files)的目录权限必须设置为文件不可执行模式,并且将上传的文件仅视为静态内容处理。

建议将上传目录设到 web 应用的根目录之外。

然后 Web 服务端设置为处理上传目录的文件时,仅默认使用静态文件进行处理。

另外,设一个文件类型白名单(例如,仅允许图像文件上传),并且文件上传时校验类型,仅限白名单中的允许上传,这样也可以一定程度上防止该问题。不过这个方法不能作为解决本问题的唯一措施。

防止浏览器远程代码执行

为了防止在客户端执行脚本,在向客户端发送要下载的文件时必须添加以下标头:

Content-Type: application/octet-stream
X-Content-Type-Options: nosniff

标头 Content-Type: application/octet-stream 表示浏览器会显示下载对话框,而不是对文件进行解析并可能执行脚本内容,例如下载的是一个 HTML 文件。

标头 X-Content-Type-Options: nosniff 则可防止浏览器尝试检测文件 mime 类型。若不设置,那尽管给定 content-type header,浏览器也会重新检测文件 mime 类型。

对于已知的安全文件,也可以改为使用一个白名单去发送 content-type header,例如,针对 PNG 文件则发送 Content-Type: image/png

防止恶意软件传播

为防止攻击者上传和传播恶意软件(例如计算机病毒),建议文件类型在安全白名单中的才允许上传。

请注意,在我们的上传处理示例中对文件类型的检测,它是基于文件扩展名而不是实际文件内容进行的。这使得攻击者仍然可能通过伪装为图像扩展名来散播恶意软件,所以我们应让客户端计算机在打开这些文件时,阻止其自动执行。

在伪装为图像进行显示的漏洞中,它根本起不到保护作用,也不能防止用户打开修改了扩展名的文件,从而无意中执行了包含其中的恶意代码。

文件上传服务的安全配置


下列配置可以让上传的文件作为静态文件处理,不过其标头需要按照前文“文件上传相关风险规避”中的标头进行设置。

请不要对这些配置进行简单的复制粘贴,你需要了解它们每一项的具体功能以确保对它们正确应用。

始终测试你的配置,确保它是安全的!

例如尝试上传 PHP 脚本并检查它们有没有被你的 web 服务端执行,可以把文件命名为“example.php”、“example.php.png”和“example.png”,内容则像以下示例:

GIF89ad <?php echo mime_content_type(__FILE__); phpinfo();
Apache 配置

将以下指令添加到 Apache 配置(例如 /etc/apache2/apache2.conf),注意要将 <Directory> 配置设为上传目录的绝对路径:

<Directory "/path/to/project/server/php/files">
  # Some of the directives require the Apache Headers module. If it is not
  # already enabled, please execute the following command and reload Apache:
  # sudo a2enmod headers
  #
  # Please note that the order of directives across configuration files matters,
  # see also:
  # https://httpd.apache.org/docs/current/sections.html#merging

  # The following directive matches all files and forces them to be handled as
  # static content, which prevents the server from parsing and executing files
  # that are associated with a dynamic runtime, e.g. PHP files.
  # It also forces their Content-Type header to "application/octet-stream" and
  # adds a "Content-Disposition: attachment" header to force a download dialog,
  # which prevents browsers from interpreting files in the context of the
  # web server, e.g. HTML files containing JavaScript.
  # Lastly it also prevents browsers from MIME-sniffing the Content-Type,
  # preventing them from interpreting a file as a different Content-Type than
  # the one sent by the webserver.
  <FilesMatch ".*">
    SetHandler default-handler
    ForceType application/octet-stream
    Header set Content-Disposition attachment
    Header set X-Content-Type-Options nosniff
  </FilesMatch>

  # The following directive matches known image files and unsets the forced
  # Content-Type so they can be served with their original mime type.
  # It also unsets the Content-Disposition header to allow displaying them
  # inline in the browser.
  <FilesMatch ".+\.(?i:(gif|jpe?g|png))$">
    ForceType none
    Header unset Content-Disposition
  </FilesMatch>
</Directory>
NGINX 配置

将以下指令添加到 NGINX 配置中,注意要将路径参数改为上传目录的绝对路径:

location ^~ /path/to/project/server/php/files {
    root html;
    default_type application/octet-stream;
    types {
        image/gif     gif;
        image/jpeg    jpg;
        image/png    png;
    }
    add_header X-Content-Type-Options 'nosniff';
    if ($request_filename ~ /(((?!\.(jpg)|(png)|(gif)$)[^/])+$)) {
        add_header Content-Disposition 'attachment; filename="$1"';
        # Add X-Content-Type-Options again, as using add_header in a new context
        # dismisses all previous add_header calls:
        add_header X-Content-Type-Options 'nosniff';
    }
}

图像处理安全配置


以下配置通过缩小图像类型集合(GIF/JPEG/PNG)来限制攻击途径,从而规避 ImageMagick 图像处理的潜在漏洞

也请考虑使用其他更安全的图像处理库,例如 libvipsimageflow

ImageMagick 配置

建议通过 policy.xml 禁用所有非必需的 ImageMagick 编码器。

为此,请找到 ImageMagick policy.xml 配置文件并添加以下内容:

<?xml version="1.0" encoding="UTF-8"?>
<!-- ... -->
<policymap>
  <!-- ... -->
  <policy domain="delegate" rights="none" pattern="*" />
  <policy domain="coder" rights="none" pattern="*" />
  <policy domain="coder" rights="read | write" pattern="{GIF,JPEG,JPG,PNG}" />
</policymap>