Host头注入是一种利用HTTP请求头中Host字段未被正确校验而引发的安全漏洞,攻击者通过篡改Host值可以实现密码重置投毒、缓存投毒、虚拟主机间的越权访问等危害。修复这个问题的核心思路有两条:一是在应用层严格校验Host头的合法性,二是在Web服务器和虚拟主机配置层面做好隔离与白名单限制。下面我会把这两条路都讲透,包括具体的配置方法、代码示例和排查步骤。

一、Host头注入到底是怎么回事

HTTP/1.1协议要求每个请求都必须携带Host头,用来告诉服务器客户端想访问哪个域名。正常情况下,浏览器会自动填入正确的域名,但攻击者可以手动构造请求,把Host改成任意值。如果服务器端没有对这个值做校验,就会把攻击者伪造的域名当成"合法请求"来处理。

最典型的场景是密码重置功能。用户点击"忘记密码"后,系统会生成一个重置链接并通过邮件发送。这个链接通常是用当前请求的Host头拼接出来的。攻击者把Host改成自己控制的域名,那么重置链接就会指向攻击者的服务器,用户一旦点击,攻击者就能拿到重置令牌,进而接管账户。

另一个常见场景是缓存投毒。CDN或反向代理根据Host头来缓存页面,攻击者用恶意Host值请求,污染缓存内容,导致后续正常用户访问时看到被篡改的页面。

二、虚拟主机配置层面的修复方案

虚拟主机环境下,多个站点共享同一个IP地址,靠Host头来区分。这种架构天然就容易受到Host头注入的影响,因为服务器默认会接受任何Host值并尝试匹配。修复的第一步就是在Web服务器层面做严格的Host白名单。

1. Nginx配置修复

在Nginx中,最直接的做法是为每个server块设置明确的server_name,并添加一个默认的"兜底"server块来拒绝非法Host。具体配置如下:

# 合法站点配置
server {
    listen 80;
    server_name www.example.com example.com;
    root /var/www/example;
    
    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }
}

# 兜底配置:拒绝所有未匹配的Host请求
server {
    listen 80 default_server;
    server_name _;
    return 444;
}

上面的default_server块会捕获所有没有匹配到具体server_name的请求,直接返回444(Nginx特有的状态码,表示服务器主动关闭连接)。如果你用的是HTTPS,同样需要在443端口做类似配置。

另外还可以用if指令做更细粒度的校验:

server {
    listen 80;
    server_name www.example.com example.com;
    
    if ($host !~* ^(www\.)?example\.com$) {
        return 444;
    }
    
    root /var/www/example;
    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }
}

2. Apache配置修复

Apache的虚拟主机配置通常在httpd.conf或sites-enabled目录下。核心思路是使用NameVirtualHost配合严格的ServerName和ServerAlias,同时确保没有通配符匹配导致的安全隐患。

# 合法站点

    ServerName www.example.com
    ServerAlias example.com
    DocumentRoot /var/www/example
    
    
        AllowOverride All
        Require all granted
    


# 兜底配置:拒绝非法Host

    ServerName default
    
        Require all denied
    

Apache 2.4以上版本推荐使用Require all denied来拒绝访问,比旧版本的Order/Deny语法更清晰。如果你的Apache启用了UseCanonicalName,建议设置为On,这样服务器会用自己配置的ServerName来构建自引用URL,而不是依赖客户端发来的Host头。

3. IIS配置修复

在IIS中,可以通过URL重写模块来限制Host头。具体操作是在网站根目录的web.config中添加规则:


  
    
      
        
          
          
            
            
          
          
        
      
    
  

三、应用层代码层面的修复方案

光靠服务器配置还不够,尤其是当你使用反向代理或CDN时,请求到达后端应用时Host头可能已经被篡改。所以应用层必须自己做校验。

1. PHP应用修复

在PHP中,可以在入口文件或公共配置中加入Host校验逻辑:


特别注意:生成密码重置链接、邮件中的链接、OAuth回调地址时,绝对不能直接用$_SERVER['HTTP_HOST'],必须用配置文件中写死的合法域名。

2. Java/Spring Boot应用修复

在Spring Boot中,可以通过拦截器或过滤器来实现:

@Component
public class HostValidationFilter implements Filter {
    
    private static final List<String> ALLOWED_HOSTS = 
        Arrays.asList("www.example.com", "example.com");
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, 
                         FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        String host = httpRequest.getHeader("Host");
        
        if (host == null || !ALLOWED_HOSTS.contains(host)) {
            ((HttpServletResponse) response).sendError(HttpServletResponse.SC_BAD_REQUEST);
            return;
        }
        chain.doFilter(request, response);
    }
}

3. Node.js/Express应用修复

const allowedHosts = ['www.example.com', 'example.com'];

app.use((req, res, next) => {
    const host = req.get('host');
    if (!allowedHosts.includes(host)) {
        return res.status(400).send('Invalid Host Header');
    }
    next();
});

四、排查和检测方法

在修复之前,你需要先确认自己的站点是否存在这个漏洞。可以用以下几种方式检测:

第一,用curl手动发送带有恶意Host头的请求:

curl -H "Host: evil.com" http://your-server-ip/
curl -H "Host: www.evil.com" http://your-server-ip/reset-password

观察返回内容是否包含evil.com相关的链接或内容。如果有,说明存在Host头注入。

第二,使用专业的安全扫描工具,如OWASP ZAP、Burp Suite等,它们都有专门的Host头注入检测模块。

第三,检查日志。在Nginx或Apache的access log中搜索包含异常Host值的请求记录,特别是那些Host值不是你自己域名的请求。

五、进阶防护:缓存与CDN层面的注意事项

如果你的站点前面有CDN或反向代理缓存,Host头注入的影响会被放大。因为CDN通常会根据Host头来区分缓存键,攻击者污染一次缓存,所有用户都会受到影响。

解决办法是在CDN层面配置Host白名单,只缓存合法域名的请求。同时,确保CDN的回源请求使用固定的、内部的Host值,而不是透传客户端的Host头。在Nginx作为反向代理时,可以这样做:

proxy_set_header Host www.example.com;

这样无论客户端发什么Host,后端收到的都是合法域名。

六、容易被忽略的细节

很多人修复了主要入口,却忽略了以下几个点:

一是子域名。如果你有api.example.com、admin.example.com等子域名,白名单里必须把它们都加上,否则攻击者用子域名的Host值也能绕过。

二是端口号。有些攻击者会在Host头里加端口号,比如evil.com:8080。你的校验逻辑要么剥离端口号再比较,要么直接用正则匹配。

三是X-Forwarded-Host头。当请求经过多级代理时,有些代理会把原始Host放到X-Forwarded-Host里。如果你的应用只检查Host而忽略了X-Forwarded-Host,攻击者可以通过这个头绕过校验。建议两个都检查,或者在最前端的代理层就把X-Forwarded-Host覆写掉。

四是HTTP/2和HTTP/3。虽然这些新协议用的是:authority伪头而不是Host头,但本质上是同一个字段的不同表现形式。如果你的服务器同时支持多协议,校验逻辑要覆盖到。

七、总结与最佳实践

Host头注入是一个看似简单但影响范围很广的漏洞。最佳的防御策略是纵深防御:在CDN层做第一道过滤,在Web服务器层做白名单兜底,在应用层做最终校验。三层都做了,基本就不会有问题。同时,定期用自动化工具扫描,把Host校验纳入代码审查清单,才能长期保持安全。不要觉得这是小问题就忽视,实际生产环境中因为Host头注入导致的账户被盗、页面篡改事件并不少见。