HTTP协议安全头部X-Content-Type-Options 导致jsonp无法加载
问题:
- 在通过调用SpringCloud Gateway网关的时候,发现在返回的头部会携带以下内容
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
- 前端在通过jsonp访问一个后端服务路由
<script type="text/javascript" src="http://gateway.com/server/callback?callback=haha"></script>
- 这时候就发生了报错,并且请求成功也有返回,但是callback不执行。
Cross-Origin Read Blocking (CORB) blocked cross-origin response http://1.xx.com:9000/test?&callback=imageResponse with MIME type text/plain. See https://www.chromestatus.com/feature/5629709824032768 for more details.
原因:
- 通过设置"X-Content-Type-Options: nosniff"响应标头,对 script 和 styleSheet 在执行是通过MIME 类型来过滤掉不安全的文件
- 服务器发送含有 "X-Content-Type-Options: nosniff" 标头的响应时,此更改会影响浏览器的行为。
官方解释:
- 如果通过 styleSheet 参考检索到的响应中接收到 "nosniff" 指令,则 Windows Internet Explorer 不会加载“stylesheet”文件,除非 MIME 类型匹配 "text/css"。
- 如果通过 script 参考检索到的响应中接收到 "nosniff" 指令,则 Internet Explorer 不会加载“script”文件,除非 MIME 类型匹配以下值之一:
"application/ecmascript"
"application/javascript"
"application/x-javascript"
"text/ecmascript"
"text/javascript"
"text/jscript"
"text/x-javascript"
"text/vbs"
"text/vbscript"
测试和复现
spring boot controller复现有问题的请求和返回
// 返回头设置成text/plain 并强制加上nosniff 进行复现
@GetMapping(value = "/test",produces = "text/plain;charset=UTF-8")
public Object test(HttpServletResponse httpServletResponse) {
Object b = "123";
httpServletResponse.setHeader("X-Content-Type-Options","nosniff");
httpServletResponse.setHeader("X-Frame-Options","DENY");
httpServletResponse.setHeader("X-XSS-Protection","1; mode=block");
return "a({\"uri\":\"a\"})";
}
前端JS
var script1 = document.createElement("script");
script1.src = "http://1.xx.com:9000/test?&callback=a";
document.body.insertBefore(script1, document.body.firstChild);
function a(response){
console.log(response)
}
浏览器渲染前端JS 就会出现如下错误
Cross-Origin Read Blocking (CORB) blocked cross-origin response http://1.xx.com:9000/test?&callback=imageResponse with MIME type text/plain. See https://www.chromestatus.com/feature/5629709824032768 for more details.
解决方案
- 服务端去除 X-Content-Type-Options: nosniff (这个可能不太现实,所以可以不考虑)
- 服务端返回头进行修改,以白名单形式绕过nosniff,使用上述的服务端代码进行修改如下(推荐)
@GetMapping(value = "/test",produces = "text/javascript;charset=UTF-8")
public Object test(HttpServletResponse httpServletResponse) {
Object b = "123";
httpServletResponse.setHeader("X-Content-Type-Options","nosniff");
httpServletResponse.setHeader("X-Frame-Options","DENY");
httpServletResponse.setHeader("X-XSS-Protection","1; mode=block");
return "a({\"uri\":\"a\"})";
}
以上解决方案2选1,推2,服务端可控。