OWASP ZAP扫描时为什么总报“缺少安全头”?
我用 OWASP ZAP 扫描自己的前端项目,每次都会提示“Missing Security Headers”,比如 X-Content-Type-Options 和 X-Frame-Options。但我已经在 Nginx 里加了这些头啊:
add_header X-Content-Type-Options "nosniff";
add_header X-Frame-Options "DENY";
add_header X-XSS-Protection "1; mode=block";
本地 curl 测试也能看到响应头正常返回,可 ZAP 还是报错,这是为啥?是不是我配置的位置不对?
根本原因:add_header 不会自动往下继承
Nginx 的 add_header 有一个恶心的地方:如果某个 location 块里已经写了任何 add_header,那上级(server 块)的 add_header 就会被完全覆盖,不会合并。
你的配置大概率放在了 server 块,但 ZAP 扫描的请求命中了某个 location(比如
/api/或者静态资源的 location),而那个 location 里有其他的 add_header(比如 Cache-Control),导致安全头全没了。排查方法:
用 curl 扫一下 ZAP 扫描的具体路径,别只扫首页:
看看哪些路径缺安全头。
解决方案:
两种改法,看你项目情况:
第一种,把安全头配到每个 location 里,麻烦但稳妥:
第二种,用 include 抽成公共配置,然后每个 location 都引用,更清爽。
还有一点:记得加
always参数,不然在 4xx/5xx 响应时安全头不会返回,ZAP 扫到错误页面还是会报缺失。改完reload Nginx,再跑一次 ZAP 看看。
你大概率是在 location 块里又加了 add_header,导致之前 server 块里配置的头部被覆盖了。Nginx 的 add_header 不会继承父级的headers,你在一个层级写了新的 add_header,之前配置的全部失效。
检查一下你的配置是不是类似这样:
解决办法很简单,把所有头部统一放在同一个层级配置,或者在 location 块里把需要的头部全部写上。
另外还有一种情况:如果你的配置在 if 条件内部,Nginx 1.7.2 之前的版本 add_header 在 if 里是不生效的。
你可以用 curl -I 看看 ZAP 访问的那个具体 URL 返回的响应头是不是真的都有,或者直接在 ZAP 的响应面板里查看,比 curl 更准——因为有些项目会区分内外网配置。
最直接的排查方法:在 ZAP 里找到那个报错的响应,右键看 Response Headers,确认实际返回的头部到底有没有。