【web】内容安全策略CSP(Content-Security-Policy)

1. 前言

内容安全策略(CSP),其核心思想十分简单:网站通过发送一个 CSP 头部,来告诉浏览器什么是被授权执行的与什么是需要被禁止的。其被誉为专门为解决XSS攻击而生的神器。

xss是跨域脚本攻击的缩写,详细可以参见《XSS跨域脚本攻击》,举例来说,可以在页面被注入非法的js脚本,然后产生不安全的后果。

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

csp不能100%杜绝xss隐患,但是可以大大减少该隐患

CSP 的实质就是白名单制度,开发者明确告诉客户端,哪些外部资源可以加载和执行,等同于提供白名单。它的实现和执行全部由浏览器完成,开发者只需提供配置。

CSP 大大增强了网页的安全性。攻击者即使发现了漏洞,也没法注入脚本,除非还控制了一台列入了白名单的可信主机。

2. CSP作用

作用主要有两点:

  • 使用白名单的方式告诉客户端(浏览器)允许加载和不允许加载的资源。
  • 向服务器举报这种越界加载了资源的行为,以便做出更加针对性的措施予以绝杀。

2.1 什么是资源

首先要解释下csp中的资源的概念,否则很难理解csp的作用,简单来说资源就是html出现的特定元素,比如css、iframe、script,这些元素都有一个共同点,就是有多种来源(因为只有多种来源,才有可能被注入,如果都是写死的,别人还怎么注入呢?),我们以script为例:

例子来源《JavaScript 实例》 “在何处插入 JavaScript”章节
在这里插入图片描述
原文可能有误,列举的“外部文件中的 JavaScript”和“外部文件夹中的 JavaScript“内容一模一样,那我们就暂且把他们归为一类。

Head 中的 JavaScript:

<head>
<script>
function myFunction() {
    
    
    document.getElementById("demo").innerHTML = "段落已被更改。";
}
</script>
</head>

Body 中的 JavaScript:

<body>
<script>
	function myFunction() {
     
     
	    document.getElementById("demo").innerHTML = "段落已被更改。";
	}
</script>
</body>

外部文件中的 JavaScript,相对路径,同源 采用相对目录引入:

<script src="/demo/myScript.js"></script>

外部 url 中的 JavaScript,绝对路径,引入一个非同源的外部js:

<script src="https://www.w3school.com.cn/demo/myScript.js"></script>

正是由于script有多种来源,csp可以增加规则,比如限制只能从同源加载js脚本,禁止从外部加载js,鬼知道外部的js里面有没有攻击脚本呢:

我们可以使用script-src满足我们的需求 ,'self',注意单引号不能丢,这个是专有名词,后面我们会详细介绍语法,作用是表示同源:

Content-Security-Policy: script-src 'self'

2.2 资源列表

看一下选项限制各类资源的加载:

base-uri: 用于限制可在页面的 <base> 元素中显示的网址。
child-src: 用于列出适用于工作线程和嵌入的帧内容的网址。例如:child-src https://youtube.com 将启用来自 YouTube(而非其他来源)的嵌入视频。 使用此指令替代已弃用的 frame-src 指令。
connect-src: 用于限制可(通过 XHR、WebSockets 和 EventSource)连接的来源。
font-src: 用于指定可提供网页字体的来源。Google 的网页字体可通过 font-src https://themes.googleusercontent.com 启用。
form-action: 用于列出可从 <form> 标记提交的有效端点。
frame-ancestors: 用于指定可嵌入当前页面的来源。此指令适用于 <frame><iframe><embed><applet> 标记。此指令不能在 <meta> 标记中使用,并仅适用于非 HTML 资源。
frame-src: 已弃用。请改用 child-src。
img-src: 用于定义可从中加载图像的来源。
media-src: 用于限制允许传输视频和音频的来源。
object-src: 可对 Flash 和其他插件进行控制。
plugin-types: 用于限制页面可以调用的插件种类。
report-uri: 用于指定在违反内容安全政策时浏览器向其发送报告的网址。此指令不能用于 <meta> 标记,这就是举报电话。
style-src: 是 script-src 版的样式表。
upgrade-insecure-requests: 指示 User Agent 将 HTTP 更改为 HTTPS,重写网址架构。 该指令适用于具有大量旧网址(需要重写)的网站。

简单归类一下:

  • 带src的,很好理解,表明和url相关,自然与同源非通用相关,即跨域概念相关

  • 不带src的,表示其他限制,比如

    block-all-mixed-content:HTTPS 网页不得加载 HTTP 资源(浏览器已经默认开启)
    upgrade-insecure-requests:自动将网页上所有加载外部资源的 HTTP 链接换成 HTTPS 协议
    plugin-types:限制可以使用的插件格式
    sandbox:浏览器行为的限制,比如不能有弹出窗口等。
    

更详细的分类,参见《Content-Security-Policy》
在这里插入图片描述

这么多指令都要写?写起来不是很麻烦,不是的。你只需要写自己要求限制的指令就行,没写的都会默认没有限制。

比如你可以仅限制js的来源,css的来源可以不限制。

但是如果我想同时限制js、css、等所有的资源来源怎么办?一个个加起来太费劲了!

简单!你可以通过指定一个 default-src 指令替换大部分指令的默认行为,也就说如果你写了default-src 指令,那其他没写的指令都会服从default-src 的规则。

default-src可以简单理解成all,置于到底包含局部元素还是包含所有的,待研究

3. csp语法

在HTTP Header上(首选)和HTML上均可使用,如何同时配置,以http为准!
在这里插入图片描述

详尽的语法可以参见参见《Content-Security-Policy》

语法:

Content-Security-Policy: <policy-directive>; <policy-directive>

在这里插入图片描述

允许配置多条<policy-directive>,多个配置中间以分号分隔;

<policy-directive>又是一个复杂结构体,对应类似如下结构:

default-src <source> <source>

允许一个或多个source来源,多个源通过空格分开.。

我们以nodejs express框架开发的服务器端为例,在响应处设置响应头,Content-Security-Policy作为key,<policy-directive>作为value:

  app.all('*', function(req, res, next) {   
    res.header("Content-Security-Policy", "default-src https://127.0.0.1:8881")
    next()
});

在这里插入图片描述

3.1 完整示例

3.1.1 复现iframe同源限制

完整的实例如下,基于《http请求中的 OPTIONS 详解 & 跨域》中的<2.1.1.1 演示简单请求被拒绝>章节:

所需文件:

express_8881.js、express_8883.js、public/index8881.html、public/index8883.html

修改代码:

改动一:修改public/index8883.html,增加一个firame引用:

        <button onclick="send()">send request to 8881</button>
<br>
         <!--增加iframe -->
        <iframe src="http://127.0.0.1:8881/public/index8881.html"></iframe>

改动二:修改express_8883.js,增加一个iframe同源限制:

 var app = express();
  app.all('*', function(req, res, next) {
    
       
   res.header("Content-Security-Policy", "child-src 'self'")
   next()
});

注意:csp v2 中frame-src被废弃了,用child-src代替,当然在最新的csp v3中,child-src也被废弃了!

在这里插入图片描述

运行,查看:

页面修改完后效果,发现加载失败:
在这里插入图片描述
打开控制台,发现有错误信息:

 Refused to frame 'http://127.0.0.1:8881/' because it violates the following Content Security Policy directive: "child-src 'self'". Note that 'frame-src' was not explicitly set, so 'child-src' is used as a fallback. index8883.html:23

在这里插入图片描述
如果去掉改动二的话,iframe是能加载成功的!

3.2 语法深入解析

3.1 default-src

我们以default-src为例讲解如何看文档:

《default-src》

片头提到了受default-src影响的所有元素列表,正好回答了我们前面的疑问。
在这里插入图片描述
语法:
default-src政策可以允许一个或多个来源,多个源通过空格分开:

Content-Security-Policy: default-src <source>;
Content-Security-Policy: default-src <source> <source>;

在这里插入图片描述
<source>,有多种语法表示,可以是以下之一:

  • <host-source> 形式,通过主机名称(经dns转化,等价于url)、或url。

    简单来说,格式类似”http://test.example.com“,其中http://可以省略,默认的就是http://;端口号可以省略,默认是80。等价于”http://test.example.com:80“

    支持*号,表示通配符

    示例:

    http://*.example.com 匹配使用http:URL方案从 example.com 的任何子域加载的所有尝试。
    纠错
    mail.example.com:443 匹配所有尝试访问 mail.example.com 上的端口443的尝试。省略了http://
    https://store.example.com 匹配所有尝试访问store.example.com,必须使用https:,省略了端口号,默认80
    

    因此,如果你的白名单端口号不是80的话, 一定要把端口列出来!

  • <scheme-source>一种模式

    细节未研究

    data:
    blob:
    
  • 专有名词

    单引号不能丢,表示特殊的语义

     'self':当前域名,允许加载同源的外部资源,需要加引号
     'none':禁止加载任何外部资源,需要加引号
    

参考:
《内容安全策略CSP(Content-Security-Policy)》

猜你喜欢

转载自blog.csdn.net/m0_45406092/article/details/114115404