如何防范HttpServletRequest的getServerName与getRequestURL方法遭伪造攻击
除了白名单校验,还有以下几种可靠的方案来获取可信的服务器名或域名:
直接读取服务器配置的固定域名
既然客户端传入的Host头不可信,最直接的方式是使用服务器/应用自身配置的可信域名。比如在Spring Boot中,可以通过配置文件定义app.trusted-domain=example.com,然后在代码中用@Value("${app.trusted-domain}")注入使用;如果是Tomcat等容器,可以读取server.xml中<Host>节点的name属性值。这种方式完全不依赖客户端请求,避免了伪造风险。利用反向代理传递可信头部
如果请求经过Nginx、Apache等反向代理,可以让代理添加一个自定义的、仅由代理设置的头部(比如X-Trusted-ServerName),将代理配置的server_name传递给后端应用。关键是要在代理层拦截客户端传入的同名头部,防止伪造:# Nginx配置示例 proxy_set_header X-Trusted-ServerName ""; # 先清空客户端传入的头部 proxy_set_header X-Trusted-ServerName $server_name; # 再设置可信值后端应用直接读取这个头部的值即可,因为只有代理能设置它,客户端无法篡改。
从HTTPS SSL证书解析域名
对于HTTPS请求,服务器的SSL证书中包含了绑定的域名信息(CN字段或SAN扩展),可以通过请求的SSL会话获取证书并解析:SSLSession sslSession = ((HttpServletRequest) request).getSession().getSslSession(); if (sslSession != null) { try { X509Certificate cert = (X509Certificate) sslSession.getPeerCertificates()[0]; // 解析证书主题DN中的CN字段 String subjectDn = cert.getSubjectDN().getName(); String domain = subjectDn.split("CN=")[1].split(",")[0]; } catch (SSLPeerUnverifiedException e) { // 处理证书验证失败的情况 } }证书是服务器预先配置的,客户端无法修改,因此解析出的域名是可信的。
基于请求真实IP映射域名
如果服务器的IP与域名是固定绑定的,可以维护一个IP到域名的映射表。先获取请求的真实IP(注意要从可信反向代理的X-Forwarded-For头部获取,避免客户端伪造IP),再通过映射表找到对应的域名:String realIp = request.getHeader("X-Forwarded-For"); if (realIp == null || realIp.isEmpty()) { realIp = request.getRemoteAddr(); } // 假设ipDomainMap是预定义的Map<String, String> String trustedDomain = ipDomainMap.get(realIp);这个方案适合IP固定的内部服务或专属部署场景。
内容的提问来源于stack exchange,提问作者shawn.123

