Skip to content

文件上传

介绍

文件上传正成为任何应用程序中越来越重要的部分,用户可以在其中上传他们的照片、简历或展示他们正在从事的项目的视频。应用程序应该能够以一种保护应用程序和用户安全的方式抵御虚假和恶意文件。

总之,要达到安全的文件上传实现,应遵循以下原则:

  • 列出允许的扩展名。只允许对业务功能进行安全和关键的扩展
  • 确保在验证扩展之前应用输入验证。
  • 验证文件类型,不要相信Content-Type 标头,因为它可能被欺骗
  • 将文件名更改为应用程序生成的名称
  • 设置文件名长度限制。尽可能限制允许的字符
  • 设置文件大小限制
  • 只允许授权用户上传文件
  • 将文件存储在不同的服务器上。如果那不可能,请将它们存储在 webroot 之外
  • 在公开访问文件的情况下,使用映射到应用程序内部文件名的处理程序(someid -> file.ext)
  • 通过防病毒软件或沙箱运行文件(如果可用)以验证它不包含恶意数据
  • 确保使用的任何库都经过安全配置并保持最新
  • 保护文件上传免受CSRF攻击

文件上传威胁

为了评估并确切了解要实施哪些控制措施,了解您所面临的问题对于保护您的资产至关重要。以下部分有望展示文件上传功能带来的风险。

恶意文件

攻击者出于恶意提供文件,例如:

  1. 利用文件解析器或处理模块中的漏洞(例如 ImageTrick ExploitXXE
  2. 使用该文件进行网络钓鱼(例如职业表格)
  3. 发送 ZIP 炸弹、XML 炸弹(也称为 billion laughs 攻击)或只是大文件以填满服务器存储空间,从而阻碍和破坏服务器的可用性
  4. 覆盖系统上的现有文件
  5. 如果文件可公开检索,可能会危及其他用户的客户端活动内容(XSS、CSRF 等)。

公共文件检索

如果上传的文件可公开检索,则可以解决其他威胁:

  1. 公开披露其他文件
  2. 通过请求大量文件发起 DoS 攻击。请求很小,但响应却大得多
  3. 可能被视为非法、攻击性或危险的文件内容(例如个人数据、受版权保护的数据等),这将使您成为此类恶意文件的宿主。

文件上传保护

验证用户内容没有灵丹妙药。实施深度防御方法是使上传过程更加困难并更加锁定服务需求和要求的关键。实施多种技术是关键和建议,因为没有一种技术足以保护服务。

扩展验证

确保在解码文件名后进行验证,并设置适当的过滤器以避免某些已知的绕过,例如:

  • 双扩展,例如 .jpg.php,它很容易绕过正则表达式\.jpg
  • 空字节,例如 .php%00.jpg,其中.jpg被截断并.php成为新的扩展名
  • 未经适当测试和审查的通用错误正则表达式。除非您对这个主题有足够的了解,否则不要建立自己的逻辑。

请参阅输入验证 CS以正确解析和处理扩展。

列出允许的扩展

确保仅使用关键业务扩展,不允许任何类型的非必需扩展。例如,如果系统要求:

  • 图片上传,允许一种符合业务需求的约定类型;
  • 简历上传,允许docxpdf扩展。

根据应用程序的需要,确保使用危害最小风险最低的文件类型。

块扩展

识别潜在有害的文件类型并阻止您认为对您的服务有害的扩展名。

请注意,阻止特定扩展本身是一种薄弱的保护方法。Unrestricted File Upload 漏洞文章描述了攻击者如何尝试绕过此类检查。

内容类型验证

上传文件的 Content-Type 由用户提供,因此不可信,因为它很容易被欺骗。虽然不应依赖它来确保安全,但它提供了快速检查以防止用户无意中上传类型不正确的文件。

除了定义上传文件的扩展名外,还可以检查其 MIME 类型以快速防止简单的文件上传攻击。

这最好通过允许列表方法来完成;否则,这可以通过阻止列表方法来完成。

文件签名验证

结合内容类型验证,可以根据应接收的预期文件检查和验证文件的签名验证。

这不应该单独使用,因为绕过它很常见也很容易。

文件名清理

文件名可以通过多种方式危及系统,或者通过使用不可接受的字符,或者通过使用特殊和受限的文件名。对于 Windows,请参阅以下MSDN 指南。有关不同文件系统及其处理文件方式的更广泛概述,请参阅维基百科的文件名页面

为了避免上述威胁,创建一个随机字符串作为文件名,例如生成一个 UUID/GUID,是必不可少的。如果业务需求需要文件名,则应对客户端(例如导致 XSS 和 CSRF 攻击的活动内容)和后端(例如特殊文件覆盖或创建)攻击向量进行适当的输入验证。应根据存储文件的系统考虑文件名长度限制,因为每个系统都有自己的文件名长度限制。如果需要用户文件名,请考虑执行以下操作:

  • 实现最大长度
  • 将字符具体限制在允许的子集中,例如字母数字字符、连字符、空格和句点
  • 如果这不可能,请阻止列出可能危及存储和使用文件的框架和系统的危险字符。

文件内容验证

如公共文件检索部分所述,文件内容可能包含恶意、不当或非法数据。

根据预期类型,可以应用特殊文件内容验证:

  • 对于图像,应用图像重写技术可以破坏任何注入图像的恶意内容;这可以通过随机化来完成。
  • 对于Microsoft 文档,使用Apache POI有助于验证上传的文档。
  • 不建议使用ZIP 文件,因为它们可以包含所有类型的文件,并且与它们相关的攻击媒介很多。

文件上传服务应允许用户举报非法内容,并允许版权所有者举报滥用行为。

如果有足够的资源,在向公众发布文件之前,应该在沙盒环境中进行手动文件审查。

在审查中添加一些自动化可能会有所帮助,这是一个苛刻的过程,应该在使用前仔细研究。一些服务(例如Virus Total)提供 API 来根据众所周知的恶意文件散列扫描文件。一些框架可以检查和验证原始内容类型,并根据预定义的文件类型验证它,例如ASP.NET 绘图库。谨防数据泄露威胁和公共服务收集信息。

文件存储位置

必须根据安全和业务要求来选择存储文件的位置。以下几点由安全优先级设置,并且包含在内:

  1. 将文件存储在不同的主机上,这样可以完全分离为用户提供服务的应用程序与处理文件上传及其存储的主机之间的职责。
  2. 将文件存储在 webroot 之外,只允许管理访问。
  3. 将文件存储在 webroot 中,并将它们设置为仅写入权限。
  4. 如果需要读取访问权限,则必须设置适当的控制(例如内部 IP、授权用户等)

以经过研究的方式将文件存储在数据库中是一项额外的技术。这有时用于自动备份过程、非文件系统攻击和权限问题。作为回报,这为性能问题(在某些情况下)、数据库及其备份的存储注意事项打开了大门,这为 SQLi 攻击打开了大门。仅当团队中有 DBA 时才建议这样做,并且此过程表明将它们存储在文件系统上是一种改进。

有些文件在上传后会通过电子邮件发送或处理,不会存储在服务器上。在对它们执行任何操作之前,必须执行本表中讨论的安全措施。

用户权限

在访问任何文件上传服务之前,应该在两个级别上对上传文件的用户进行适当的验证:

  • 认证级别
  • 用户应为注册用户,或可识别用户,以便为其上传能力设置限制和限制
  • 授权级别
  • 用户应具有访问或修改文件的适当权限

文件系统权限

按照最小权限原则设置文件权限。

文件的存储方式应确保:

  • 允许的系统用户是唯一能够读取文件的用户
  • 仅为文件设置所需模式
  • 如果需要执行,作为安全最佳实践,需要在运行文件之前扫描文件,以确保没有可用的宏或隐藏脚本。

上传和下载限制

应用程序应为上传服务设置适当的大小限制,以保护文件存储容量。如果系统要提取或处理文件,则在进行文件解压缩后并使用安全方法计算 zip 文件大小时,应考虑文件大小限制。有关这方面的更多信息,请参阅如何安全地从 ZipInputStream 中提取文件,这是 Java 的输入流以处理 ZIP 文件。

如果可用,应用程序还应为下载服务设置适当的请求限制,以保护服务器免受 DoS 攻击。

Java 代码片段

Dominique 为 Java 中的某些文档类型编写的文档上传保护存储库。