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.  

解决方案

  1. 服务端去除 X-Content-Type-Options: nosniff (这个可能不太现实,所以可以不考虑)
  2. 服务端返回头进行修改,以白名单形式绕过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,服务端可控。

郝先生

继续阅读此作者的更多文章