Drupal 9:防止枚举攻击
Wired最近发表的一篇有关Parler数据黑客的文章谈到了黑客组织如何能够使用不安全的直接对象引用(IDOR)或枚举攻击从Parler网站窃取公开可用的信息。此类攻击涉及黑客查看站点的结构,并尝试通过查看URL来猜测下一个可用资源。显然,通过简单地枚举其公开可用帖子的ID即可下载TB级的Parler数据。
以Drupal中的任意示例为例,假设您有一个称为post的内容类型,该内容类型是使用URL中内容的类型和ID加载的。这意味着,当用户访问帖子时,他们可能会看到带有路径/post/1的URL。如果用户想查看站点上的下一个帖子,那么他们可以将ID增加一个,然后查看是否加载了下一个帖子。仅通过查看帖子的ID即可构建脚本来下载网站上的每个帖子,这几乎变得微不足道。这几乎是Parler攻击中发生的事情,尽管该攻击是通过其API服务而不是通过其网站进行的,但原理是相同的。
您可能不会认为这是个大问题,但是实际上您可能会从这样的站点泄漏很多信息,甚至没有意识到。用户配置文件尤其成问题,因为对用户配置文件页面的控制不当可能意味着站点可以泄漏其所有用户数据,而无需黑客入侵系统。不过,它不必是用户个人资料,许多用户会在公共帖子中发布可识别的信息,因此任何允许列举所有这些帖子的系统都会泄漏很多可以轻松链接回用户的信息。
Twitter具有一个很好的机制,可以为每个推文分配一个唯一的长号,以防止对其推文进行枚举攻击。鉴于每分钟发送到Twitter的帖子数量众多,这是一项工程学壮举,无需冲突即可正确进行。例如,如果我使用最近的Tweet,并查看比Tweet的ID高一低的数字,那么我什么也找不到。
https://twitter.com/philipnorton42/status/1354010770338697215 <- 404 https://twitter.com/philipnorton42/status/1354010770338697216 <- an actual tweet https://twitter.com/philipnorton42/status/1354010770338697217 <- 404
这使我无法循环浏览所有可以找到的数据推文。很长一段时间以来,您将被Twitter阻止,然后您才能通过枚举攻击找到下一个Tweet。您可以尝试通过搜索从API中获取大量数据,但是Twitter对可以执行的请求数量有非常谨慎的使用限制,因此您将无法在阻止之前下载Tweets的任何重要部分。
开箱即用,Drupal很容易受到这种攻击,因为所有内容都倾向于以相同的方式引用。通常,您会看到一个使用实体类型构造的URL,后跟该实体的ID(例如,/node/1)。页面,分类术语,用户和在Drupal中创建的大多数实体都是这种情况。有趣的是,Drupal安全团队并不认为这是一个问题,我倾向于在这里同意他们的回应。Drupal旨在成为一个Web框架,并允许通过以不同方式将组件插入在一起来创建各种站点。Drupal确实非常擅长成为社区驱动的网站,并且通过实现用户个人资料页面,审核的评论和广泛的权限系统等许多功能来实现此目标。仅当页面出现时,这种信息泄漏才成为问题本身包含安全或可识别个人身份的信息,由网站所有者根据此信息采取相应措施。
如果您有兴趣为您的Drupal网站防止这种攻击或信息泄漏,那么我整理了一份您可以开始研究的内容清单。从这一点开始,我将假设您想锁定您的内容,以便仅通过唯一的URL查找公开帖子,并且无法访问用户个人资料页面。
用户个人资料权限
Drupal将在路径“user/uid”下创建用户个人资料页面,其中uid是用户的ID。这些页面包含用户名以及它们成为站点成员的时间。非管理员帐户将禁用访问用户个人资料页面的默认权限(称为“查看用户信息”),因此,如果希望彼此之间或公众之间都可以看到用户,则需要主动将其打开。这意味着,如果非管理员用户尝试访问用户帐户页面,则将收到拒绝访问(403)错误。这不会阻止任何人猜测您拥有多少个用户帐户及其用户ID,但是默认情况下至少他们的信息是安全的。
即使您不允许匿名用户查看用户个人资料页面,严格限制将出现在页面上的数据也是一个很好的主意。如果为用户创建字段,则需要绝对确保信息安全,并且仅在绝对需要时才打印到用户配置文件中。我已经看到许多站点具有用户的元信息或私有字段,因此,如果您具有该类型的字段,则需要确保它们对用户是隐藏的,除非需要。Drupal没有开箱即用的字段级别权限,但是您可以轻松地使用ahook_form_alter()来操纵用户表单以隐藏字段。防止将它们打印出来比较容易,这仅意味着显示格式不包含这些字段。
幸运的是,没有直接的方法可以将Drupal配置为在Drupal站点上打印出用户的电子邮件地址。为此,您将需要主动安装模块或编写模板代码。从垃圾邮件的角度来看,不要将用户的电子邮件地址输出给匿名用户,这一点很重要。互联网上有一些专门用于查找电子邮件地址的漫游器,通过暴露用户的电子邮件地址,您使它们成为电子邮件垃圾邮件的目标。
路径自动
Pathauto模块几乎是Drupal项目的默认要求,可以通过简单地遮盖URL来防止这种类型的攻击。如果您确实允许在网站上显示用户个人资料,则可以使用pathauto轻松地防止枚举用户个人资料,并从URL结构中删除ID。
最基本的方法,或者在创建社区驱动的网站时,是使用包含用户名的路径。这样的事情很普遍。
我觉得我应该指出,公开这样的用户名可以使攻击者更轻松地访问用户的帐户,因为他们现在将拥有所需信息的一半。他们所需要做的就是猜测密码,然后他们就进入了帐户。但是,Drupal具有内置的防洪机制,以洪水服务的形式提供。这意味着,如果攻击者试图闯入该用户的帐户,则他们只能在阻止该帐户之前猜测几次密码。在使最终用户烦恼的同时,它确实防止了他们的帐户被盗用。要阅读有关使用Flood服务的更多信息,请参阅我以前的文章,关于将洪水注入Drupal表单。
与适当的权限结合使用的Pathauto可能会掩盖并阻止访问您站点中的用户配置文件。替代方法是查看在用户的配置文件路径中包含哈希或UUID,尽管该方法未内置在Drupal中,因此您需要创建代码才能做到这一点。值得庆幸的是,确实存在一个称为令牌UUID的模块,该模块使您可以将Drupal中每个对象的实体UUID用作令牌,因此可以进行每次Pathauto集成。
兔子洞
关于路径,Pathauto只是故事的一半,另一半由RabbitHole模块填充。
Drupal公开了许多连接到实体的不同路径,即使您正确设置了权限,您也可能会泄漏有关拥有多少用户以及其他一些结构碎片的信息。许多Drupal网站还使用诸如分类学术语之类的东西来对网站内的内容进行细分。默认情况下,所有分类法术语都提供一个路径,因此用户可以猜测URL并查看站点中所有内部类别的列表。不仅如此,而且如果您的普通用户碰巧访问结构分类法页面,则很有可能该页面的主题设置不正确,并且他们会获得不好的体验。
RabbitHole模块可让您限制甚至隐藏对站点上所有形式的实体的访问。这意味着用户将获得404,而不是意外访问结构分类页面。
RabbitHole阻止用户枚举的作用是在尝试查看用户个人资料时将拒绝访问响应(403)更改为页面未找到响应(404)。这使得无法枚举您的用户,因为未找到的正常页面和用户的个人资料之间不会有任何区别。如果您在“管理”-“配置”-“人员”-“帐户设置”表单中将用户实体设置为像这样进行配置,则将具有所需的效果。
我一直在Drupal站点上使用RabbitHole模块,它在减少Drupal的占地面积方面非常方便。它对于内容页面的用处不大,但确实可以防止意外的URL暴露。
用户名枚举防止
如果您担心用户周围的信息泄露,则可以使用“用户名枚举阻止”模块。该模块具有许多不同的功能,但是将阻止任何人试图猜测您拥有多少用户以及他们的用户名是什么。首先,要防止使用正常的用户路径,但要防止用户登录和注册表单公开有关用户的信息。这意味着,如果用户尝试使用电子邮件地址登录,则不会告诉他们有关其登录尝试的任何可识别信息。错误消息不会显示“您的密码不正确”,而是显示“用户名或密码不正确”,并且不会泄露该用户实际上是一个用户。
该模块还具有与RabbitHole模块类似的功能,在查看用户的个人资料页面时,它将自动生成404响应而不是403响应。它还在路径重定向结构中插入了一些缝隙,Drupal会在该结构中帮助将页面从/user/1重定向到正确的规范页面,即使最终找不到该页面也是如此。由于这为用户在该地址处的存在提供了一些线索,因此这是一个重要的预防步骤,可确保完全不会猜测用户。
当查看处理敏感用户数据的站点时,添加此模块尤为重要。诸如专业兴趣网站,约会网站甚至是出售酒精的网站之类的东西,在泄漏用户注册内容时应格外小心。
JSON:API
到目前为止,我一直专注于通过前端接口处理枚举攻击。同样重要的是,确保您的DrupalAPI层是安全的。默认情况下,JSON:API模块将启用到您站点中各种对象的一堆路径。例如,您可以访问/jsonapi/user/user之类的URL,以查看该站点上可用的用户列表。在JSON:API接口中,实体和字段访问以及任何其他验证约束都受到尊重。从本质上讲,这意味着没有身份验证就无法获取数据并将其发布到API。
尽管JSON:API由Drupal权限和访问系统控制,但是由于您可以要求它提供整齐的用户帐户分页列表,因此它在某种程度上容易受到信息泄漏的影响。值得庆幸的是,这些用户帐户是由权限系统控制的,因此他们只能获得一个显示名称。同样重要的是要知道JSON:API不会使用UID来查找用户。而是由用户的UUID返回用户记录,因此要查找有关特定用户的信息,您需要使用URL/jsonapi/user/user/dd0d36fb-d136-4e72-982e-545294ae9ad8。
JSON:API模块没有很多接口,但是您可以安装JSON:APIExtras模块以查找有关公开哪些端点以及包括哪些数据字段的更多信息。
安装了JSON:APIExtras模块后,您可以确切地看到已启用和禁用了哪些端点。我强烈建议您禁用对所有内容的访问,除非您有使用它的特定要求。尽管“以防万一”使端点处于启用状态似乎是一个好主意,但实际上,这只是数据的另一个攻击面。
除了阻止访问某些终结点外,将速率限制添加到您的API中也是一个好主意。速率限制是对用户可以执行的每分钟请求数设置限制的一种做法,这可以防止用户占用您网站上过多的资源。可以安装“速率限制”模块并将其配置为执行此操作。该模块基于DrupalFlood系统构建,因此可用于阻止任何用户或IP地址无限制地访问您Drupal站点上的任何路由。该模块是高度可配置的,可插入到Drupal的不同区域。这不仅是为JSON:API模块构建的,而且是限制不受限制地访问API的一种很好的方法。
结论
从根本上讲,如果您运行Drupal站点,则枚举攻击(或任何形式的攻击)应该始终在您的雷达范围内。您需要知道您的Drupal网站具有什么样的“形状”,就好像您忘记了一个泄漏信息的角落一样,攻击者很有可能会找到它并加以利用。仅仅因为信息隐藏在URL和顺序ID的后面并不意味着它是安全的。另外,请勿仅使JSON:API发挥作用而不对其进行任何操作。它只会创建另一个攻击面,如果您未正确管理,可能会导致灾难。
您应该始终进行测试,以仔细检查匿名用户无法完全访问您认为安全的页面。这意味着,在您的网站上将来发生的任何破坏安全性的更新都可以在上线之前被捕获。诸如“作为匿名用户,尝试访问用户个人资料页面时应获得404”之类的简单内容应该是您的测试套件的一部分。
诸如Pathauto和RabbitHole之类的模块通常安装在Drupal站点上,尽管它们确实为您的路径提供了很好的安全网,但是您需要确保正确配置它们。创建一个新的结构性内容类型(例如Web表单或FAQ问题),而不是限制该内容类型具有的路径,这是一个非常简单的错误。这可以撤消您已进行的所有仔细配置,因此,如果添加任何新内容,则将其添加到枚举测试中。
即使您正在构建一个允许公开访问帖子和用户个人资料的社区站点,您也应该安装一些保护措施以防止下载整个站点。安装速率限制模块是防止此类事情发生的第一步。但是,您还应该将API锁定在某种形式的身份验证之后,以防止用户使用代理服务器绕过您施加的限制。
有多种不同的技术可以保护您的站点免受枚举攻击,但是必须将它们用作一致的策略,而不是单独的部分。不要仅仅安装Pathauto模块并认为问题已经解决,因为您还需要另一个模块来例如防止访问结构分类标签。