网页的安全第一

这篇文章上次修改于 3 个月前,可能部分内容已经不适用,如有疑问可询问作者。

网页的安全第一

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标签的URLbase-uri 'self'无防止base标签劫持
form-action控制表单提交的URLform-action 'self'无防止表单劫持
frame-ancestors控制哪些页面可以嵌套当前页面frame-ancestors 'none'无防止点击劫持
worker-src控制Worker和SharedWorkerworker-src 'self'继承script-srcWeb Worker脚本
manifest-src控制PWA清单文件manifest-src 'self''self'Progressive Web App清单

上面的每一项都有很多值可以设置,大部分的默认值都是self,即同源策略,在这之外,还可以进行更细粒度的控制。 注意这种设置不是一对一的,是备选状态,前面的设置如果不生效,才会使用后面的设置。

HTML
Content-Security-Policy: script-src 'unsafe-inline' 'self'
Copy
源值作用示例使用场景
'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 URIimg-src data:内联图片
blob:允许blob URIworker-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. 阻止内联脚本执行

HTML
Content-Security-Policy: script-src 'self'; 
object-src 'none'; base-uri 'self'
Copy
HTML
<!-- 这些都会被CSP拦截 -->
<script>alert('XSS')</script> <!-- 内联脚本被拦截 
-->
<script src="https://evil.com/malware.js"></
script> <!-- 外部脚本被拦截 -->
<img onerror="alert('XSS')" src="x"> <!-- 内联事
件被拦截 -->
Copy
  1. 阻止动态代码执行
JS
// 这些都会被CSP拦截
eval("alert('XSS')"); // 被unsafe-eval限制
new Function("alert('XSS')"); // 被拦截
setTimeout("alert('XSS')", 1000); // 字符串参数被
拦截
Copy
  1. 阻止数据注入攻击 html Content-Security-Policy: img-src 'self';  style-src 'self'; font-src 'self'
HTML
<!-- 这些注入都会被拦截 -->
<img src="data:text/html,<script>alert('XSS')</
script>"> <!-- data:被限制 -->
<link rel="stylesheet" href="https://evil.com/
Copy

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出现之前就已经存在,简单需求不需要预检,从而降低了攻击的难度。