服务端必须校验Host头是否合法:强制白名单字符串匹配,禁用反向DNS查询,启用DNS重绑定防护,禁止动态拼接URL和信任客户端域名上下文。

服务端必须校验 Host 头是否合法
浏览器发起请求时,Host 头由客户端控制,攻击者可随意伪造。DNS重绑定成功后,恶意域名仍保持不变,但解析 IP 已跳转到内网地址(如 127.0.0.1 或 192.168.1.100),此时若服务端只认域名、不校验 Host 值,就会误认为是合法外部请求。
正确做法是在请求入口处强制检查 Host 头是否落在白名单中:
- 使用严格字符串匹配(不建议通配符或正则,易绕过)
- 拒绝空
Host、IP 形式域名(如127.0.0.1:8080)、含端口的非常规格式 - 对非生产环境(如本地开发)可临时放宽,但上线前必须关闭
示例逻辑(伪代码):
if (request.host() != "api.example.com" &&
request.host() != "www.example.com") {
return HttpResponse::Forbidden();
}
避免在业务逻辑里依赖 DNS 反查(gethostbyaddr)
有些 C++ 服务会用 gethostbyaddr 或 getnameinfo 对客户端 IP 做反向 DNS 查询,再基于返回的域名做权限判断——这正是 DNS 重绑定的突破口。攻击者只要让自己的恶意域名能反解到该 IP,就能伪造身份。
真实场景中应:
- 完全禁用反向 DNS 查询用于鉴权或路由决策
- 如需识别客户端来源,优先用已知可信的代理头(如
X-Forwarded-For,但需配合前置 Nginx 校验) - 若必须用域名信息,只接受由内部 DNS 服务器解析的结果,并缓存 TTL > 300 秒,避免频繁重绑干扰
启用 TransportSecuritySettings::enable_dns_rebinding_protection
如果你用的是支持该配置的 C++ Web 框架(如基于 TransportSecurityMiddleware 的定制栈),直接启用内置防护是最省事的方式。它会在中间件层拦截 Host 不匹配或解析 IP 不在允许范围内的请求。
关键点:
- 该开关默认通常为
false,必须显式设为true - 配套必须设置
allowed_hosts(字符串 vector)和allowed_origins(如有 CORS) - 注意:它不替代
Host校验,而是多一层防御;若两者都配错,可能造成误拦或漏防
初始化示例:
TransportSecuritySettings settings;
settings.enable_dns_rebinding_protection = true;
settings.allowed_hosts = {"api.example.com", "admin.example.com"};
不要信任客户端传来的任何“当前域名”上下文
常见错误是把前端 JS 通过 window.location.hostname 拼接的 URL、或从 Referer 头提取的域名,直接当作可信源用于后端资源加载或跳转。DNS 重绑定后,这些值看起来仍是原始域名,但实际流量已打到内网。
更危险的是这类写法:
std::string target = "http://" + request.query_param("domain") + "/health";
http_get(target); // SSRF 高危!
这类逻辑必须全部重构为:
- 硬编码目标地址(如只允许访问
http://internal-auth:8080/health) - 或使用服务发现机制(如 Consul DNS SRV 记录),且只解析内部可信域名
- 所有动态拼接的 HTTP 请求,必须经过白名单 URI 模式匹配(不是简单字符串包含)
最常被忽略的一点:日志中打印 Host 或 Referer 并不危险,但若把这些字段内容写进配置文件路径、数据库表名、或作为系统命令参数,就立刻变成高危注入点。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











