网页的安全第一
csp严格模式
CSP(Content Security Policy)是一种通过HTTP响应头实施的安全机制,用于防止跨站脚本攻击(XSS)和其他代码注入攻击。严格模式是CSP 3.0引入的高级安全策略,它通过引入白名单的方式,限制了页面中可以加载的资源,来确保网站只会加载可信的内容。
常见指令和示例
很多很多 基本html里加载的所有东西都可以去控制 用的最多的事script-src、frame-ancestors还有connect-src
script-src用来保证不被一些恶意脚本攻击,frame-ancestors用来防止自己的网页被嵌套在其他网页中,connect-src用来防止爬虫之类的来攻击网站。
| 指令 | 作用 | 示例 | 默认值 | 备注 | 
|---|---|---|---|---|
| script-src | 控制JavaScript脚本的加载源 | script-src 'self' https://cdn.example.com | 'self' | 最重要的指令,控制脚本执行 | 
| style-src | 控制CSS样式表的加载源 | style-src 'self' 'unsafe-inline' | 'self' | 控制样式表和内联样式 | 
| img-src | 控制图片资源的加载源 | img-src 'self' data: https: | 'self' | 包括img标签和CSS中的图片 | 
| font-src | 控制字体文件的加载源 | font-src 'self' https://fonts.gstatic.com | 'self' | Web字体文件 | 
| connect-src | 控制AJAX、WebSocket等连接 | connect-src 'self' https://api.example.com | 'self' | 控制网络连接 | 
| frame-src | 控制iframe的加载源 | frame-src 'self' https://youtube.com | 'self' | 控制嵌套页面 | 
| object-src | 控制插件(如Flash)的加载源 | object-src 'none' | 'self' | 通常设为'none'禁用插件 | 
| media-src | 控制音视频媒体的加载源 | media-src 'self' https://cdn.example.com | 'self' | audio和video标签 | 
| default-src | 所有未指定指令的默认值 | default-src 'self' | 无 | 作为其他指令的fallback | 
| base-uri | 控制base标签的URL | base-uri 'self' | 无 | 防止base标签劫持 | 
| form-action | 控制表单提交的URL | form-action 'self' | 无 | 防止表单劫持 | 
| frame-ancestors | 控制哪些页面可以嵌套当前页面 | frame-ancestors 'none' | 无 | 防止点击劫持 | 
| worker-src | 控制Worker和SharedWorker | worker-src 'self' | 继承script-src | Web Worker脚本 | 
| manifest-src | 控制PWA清单文件 | manifest-src 'self' | 'self' | Progressive Web App清单 | 
上面的每一项都有很多值可以设置,大部分的默认值都是self,即同源策略,在这之外,还可以进行更细粒度的控制。 注意这种设置不是一对一的,是备选状态,前面的设置如果不生效,才会使用后面的设置。
Content-Security-Policy: script-src 'unsafe-inline' 'self'| 源值 | 作用 | 示例 | 使用场景 | 
|---|---|---|---|
'self' | 同源策略 | script-src 'self' | 只允许同源资源 | 
'none' | 完全禁止 | object-src 'none' | 禁用某类资源 | 
'unsafe-inline' | 允许内联代码 | style-src 'unsafe-inline' | 允许内联样式/脚本 | 
'unsafe-eval' | 允许eval()等 | script-src 'unsafe-eval' | 允许动态代码执行 | 
'nonce-xxx' | 特定nonce的脚本 | script-src 'nonce-abc123' | 精确控制单个脚本 | 
'sha256-xxx' | 特定hash的脚本 | script-src 'sha256-hash' | 基于内容hash控制 | 
'strict-dynamic' | 信任传递 | script-src 'strict-dynamic' | 现代CSP核心特性 | 
data: | 允许data URI | img-src data: | 内联图片 | 
blob: | 允许blob URI | worker-src blob: | 动态创建的Worker | 
filesystem: | 允许文件系统 | img-src filesystem: | 本地文件系统资源 | 
这里用的最多的事nonce,对于一些大型网站,直接将所有内联脚本、内联样式的写法禁用掉是非常困难的,但是我们又不能直接把 unsafe-inline 添加回来,因此给出了一个白名单的方案——我们每次打包的时候生成一个随机加密字符串,让script、src标签上的nonce值和CSP头里的nonce值一致,这样就可以实现白名单的效果。
上面这个法子可以解决直接打包的结果,但是针对动态加载的脚本,在创建的时候还是没法执行,这时候就要配合 strict-dynamic 来使用了。添加了 strict-dynamic 之后,在可信(nonce 可以对应上)的 script 中动态创建的 script 也可以正常执行。
xss
讲完了这么多,csp主要是为了预防xss攻击 1. 阻止内联脚本执行
Content-Security-Policy: script-src 'self'; 
object-src 'none'; base-uri 'self'<!-- 这些都会被CSP拦截 -->
<script>alert('XSS')</script> <!-- 内联脚本被拦截 
-->
<script src="https://evil.com/malware.js"></
script> <!-- 外部脚本被拦截 -->
<img onerror="alert('XSS')" src="x"> <!-- 内联事
件被拦截 -->- 阻止动态代码执行
 
// 这些都会被CSP拦截
eval("alert('XSS')"); // 被unsafe-eval限制
new Function("alert('XSS')"); // 被拦截
setTimeout("alert('XSS')", 1000); // 字符串参数被
拦截- 阻止数据注入攻击
html Content-Security-Policy: img-src 'self'; style-src 'self'; font-src 'self' 
<!-- 这些注入都会被拦截 -->
<img src="data:text/html,<script>alert('XSS')</
script>"> <!-- data:被限制 -->
<link rel="stylesheet" href="https://evil.com/csrf
csrf攻击的原理是利用用户的登录状态,在用户没有退出登录的情况下,访问一个第三方网站,第三方网站会以用户的名义发送请求到服务器,从而实现攻击。
防范的话可以从head配置做起:
Cookie SameSite不能设置为none
SameSite 属性用于定义浏览器是否允许 Cookie 在跨站请求中被发送,如果设置为了false,那么在跨站请求中,Cookie 会被发送,从而造成cookie的泄露,如果
后端接口的Access-Control-Allow-Origin 禁止设置宽松
CSRF 攻击通常只能在跨域环境中发生,CORS 机制允许服务器明确告诉浏览器某些请求是否允许跨域。Access-Control-Allow-Origin 头用于指定允许哪些来源可以访问服务器资源。在 Access-Control-Allow-Origin 配置宽松(如配置为 * 或者请求 Origin)的情况下,就相当于原本无法在跨域环境发出的接口具备了 CSRF 攻击条件。
接入token机制
让前端请求每次都需要携带一个token,后端接口在收到请求时,需要校验token是否有效,从而实现防护。 如果攻击者成功通过一些手段获取到了header里的token,再发送请求时,由于不满足了简单请求的条件,会被识别为复杂请求,触发CORS预检请求(OPTIONS方法)。 如果服务器的CORS配置严格(如文档前面提到的 Access-Control-Allow-Origin 不能设置宽松),这个预检请求就会被拦截,这样即使攻击者获取了Token,也无法成功发送跨域攻击请求。 而简单请求的定义包含了传统HTML表单能发送的所有请求类型,这些在CORS出现之前就已经存在,简单需求不需要预检,从而降低了攻击的难度。