我有一个 Apache/2.4.6 (CentOS) 服务器,其中有多个子域作为 Apache VirtualHost 中的 ServerAlias。
类似于:
<VirtualHost *:443>
ServerName mydomain.com
ServerAlias a.mydomain.com
ServerAlias b.mydomain.com
每个客户公司都应通过其子域进行访问,并且为了安全起见,每个客户公司都有不同的数据库,数据是分离的。
一位网络安全专家提醒我,存在一个漏洞,一个子域“a.mydomain.com”的用户可以通过在从客户端到 Web 服务器的调用中添加 Host 标头来访问另一个子域“b.mydomain.com”。
一开始我尝试用 PHP 获取信息,但失败了,PHP 没有获取到 headers 信息。然后我转而寻找在 Web 服务器级别(Apache)上解决此问题的方法。
当恶意用户试图欺骗服务器并使用主机头将请求发送到另一个子域时,我想要检测并拒绝,在此示例中,用户应该由 a.mydomain.com 而不是 b.mydomain.com 提供服务:
curl 'https://a.mydomain.com/users/login' \
-H 'Host: b.mydomain.com' \
--data-raw $'{"email":"[email protected]","password":"*****"}'
来自客户端应用程序的正常调用如下所示:
curl 'https://a.mydomain.com/users/login' \
--data-raw $'{"email":"[email protected]","password":"*****"}'
我尝试过RequestHeader unset host
,但是它并没有像我预期的那样工作。
我的预期是,如果恶意用户发送了“Host”标头,服务器应该会忽略它。这将导致上述两个 culr 调用实际上相同。
我认为发生的情况是 Apache 在调用中使用了 URL,但是如果存在“Host”标头,则它将优先使用并且使用该标头,而 URL 中的原始域将被丢弃。
如果是这种情况,则RequestHeader unset host
不会向我的 PHP 代码发送任何主机,这会导致我的代码中断,因为它需要知道哪个客户公司正在调用它。
这没有意义,因为
Host
Apache 首先通过标头来了解 URL。常规 HTTP 请求不会向 Web 服务器提供文字 URL。如果您使用
-v
选项运行 curl,您会看到请求看起来像GET /users/login
,即仅存在路径(和查询字符串)——它可能与您想象的不同GET https://a.mydomain.com/users/login
。(代理使用后者,普通请求不使用。)那么,当发出 HTTP 请求时,URL 的主机名部分会去哪里呢?就 HTTP 而言,它唯一
Host:
去的地方是标头。因此,标头不可能“不同于请求的 URL”,因为它就是请求的 URL。换句话说,更改 Host 标头实际上并不比更改 URL 更能“欺骗服务器”;只需这样做
https://b.mydomain.com/users/login
就会产生同样的效果。可能还有一些其他问题“专家”试图警告您(例如,也许他们正在谈论尝试发送两个Host 标头的请求,我相信 Apache 和 PHP 都已经对此进行了防范;或者也许他们的意思是服务器接受不匹配的 TLS SNI 和 HTTP Host,这本身也不是什么大问题),但您需要向他们寻求澄清。
我大胆猜测,您的应用程序不会按域隔离 PHP 会话(或身份验证令牌),因此,如果用户登录并收到域 A 的会话 cookie(或访问令牌),但随后尝试发送域 B 的相同令牌,则应用程序会接受它作为域 B 的有效身份验证。这将是一个重大的安全问题,但与欺骗 Host 标头无关。
现在,PHP确实知道它被调用的是哪个域(
$_SERVER["SERVER_NAME"]
和/或$_SERVER["HTTP_HOST"]
- 前者来自 Apache,后者实际上是标Host:
头;PHP 实际上通过字段接收所有标头HTTP_*
),所以如果我猜对了,那么可能需要存储这些字段并将其与 PHP 会话进行比较(或与您发布的任何 JWT 的受众或您用于身份验证的任何其他内容进行比较)。但同样,这只是一个猜测,你应该请专家为你提供更具体的解释,说明它们的真实含义。
您误解了这里的漏洞,以顶部帖子中的示例为例:
这将通过 SNI 向服务器进行身份验证
a.mydomain.com
,然后发送主机头b.mydomain.com
。Apache 默认会检测到这种情况,将错误记录到日志文件并返回 400 错误。
另请参阅: https: //security.stackexchange.com/questions/134021/what-kind-of-attack-is-prevented-by-apache2s-error-code-ah02032-hostname-prov
Apache 在变量中公开检测到的 SNI 的结果
SSL_TLS_SNI
请注意,您正在使用
ServerAlias
而不是为每个服务器创建单独的块。如果 SNI 告知它已连接到a.example.com
,然后发送 Host 标头b.example.com
,因为从 apaches 的角度来看,它是允许请求的同一台服务器。您希望拆分每台服务器,以确保 Apache 能够检测到 SNI 和主机头的不匹配。您可以使用块
Include
来加载通用配置文件,这样您就不必重复