1、Nginx相关
1.1、升级Nginx版本
及时升级Nginx版本,新版本包含对旧版本的漏洞修复
1.2、版本号隐藏
版本号的显示也被扫描软件识为一种不安全的行为
2、具有不安全、不正确或缺少SameSite属性的Cookie
可以直接在Nginx下设置
location / {add_header Set-Cookie "Path=/; HttpOnly; SameSite=Strict";
}
3、加密会话(SSL)Cookie中缺少Secure属性
可以直接在Nginx下设置
location / {add_header Set-Cookie "Path=/; HttpOnly; SameSite=Strict; Secure";
}
4、跨站点脚本攻击
4.1、反射型
所谓反射,是指此种类型的注入脚本必须被包含在发往Web服务器的请求中,然后Web服务器以某种方式反射到用户浏览器执行。
一个简单例子
https://example.com/index.php?user=<script>恶意代码</script>
上述代码被攻击者加了恶意代码,当用户请求上述代码,服务器没有对用户输入进行充分的验证或转义,服务器可能会直接将这些代码插入到返回的HTML中
处理方式
- 通过网关对输入进行转义;
- 保证正确的响应Content-type;
对安全扫描软件来说,要保证过程和结果都不能有问题。即使携带恶意脚本的响应没有实际在页面被解析执行,但是如果接口响应Content-type此刻为html,这就满足了脚本可执行的基本条件,对于扫描软件来说,这就是有问题的case
4.2、存储型
注入的脚本永久存储在Web服务器上,如数据库、内存或文件系统中。只要注入脚本代码没有被清理,每次用户访问网页时都将加载恶意脚本。
这个最常见的就是富文本编辑器,富文本编辑器内携带了html元素,如果处理不当,很容易被攻击利用。当然解决方法比较简单,对于前端侧,可以直接利用类似sanitize-html这种内容处理库进行处理
5、“Content-Security-Policy”头中缺少“Script-Src”或“Default-src”策略或相应策略不安
此问题同“Content-Security-Policy”头缺失
一并处理。这个问题的处理要繁琐的多,先引用几个基本概念
内容安全策略
内容安全策略 (CSP) 对于保护应用程序免受各种安全威胁(例如跨站点脚本 (XSS)、点击劫持和其他代码注入攻击)至关重要。
通过使用 CSP,开发者可以指定哪些来源是允许的,例如内容来源、脚本、样式表、图片、字体、对象、媒体(音频、视频)、iframe 等等。
Nonce
一个 Nonce 是一个为一次性使用而创建的唯一随机字符串。它与 CSP 结合使用,可以选择性地允许某些内联脚本或样式执行,绕过严格的 CSP 指令。本修改主要涉及到Nonce的生成和配置。简单来说,Nonce为应用的各种资源生成了随机值,他们被加到脚本样式表的标签上,当值与响应头的
Content-Security-Policy
下配置的Nonce匹配的时候,浏览器才可加载此资源,否则阻止加载
使用Nonce可以以较为严格的方式解决此问题,算是推荐的做法
对于前端来说,Nonce的生成需要借助各种打包工具
打包工具 | Nonce生成Plugin |
---|---|
Webpack | csp-html-webpack-plugin |
Vite | vite-plugin-csp |
下面以Webpack举例来详述配置步骤,Vite同理
安装依赖
配置Webpack
...
const HtmlWebpackPlugin = require('html-webpack-plugin')
const CspHtmlWebpackPlugin = require('csp-html-webpack-plugin')
...
const crypto = require('crypto')
const emotionalNonce = crypto.randomBytes(16).toString('base64')
const emotionalNonce1 = crypto.randomBytes(16).toString('base64')
const emotionalNonce3 = crypto.randomBytes(16).toString('base64')const webpackConfig = merge(baseWebpackConfig, {plugins: [new HtmlWebpackPlugin(),new CspHtmlWebpackPlugin({'base-uri': "'self'",'object-src': "'none'",'script-src': ["'self'", `'nonce-${emotionalNonce1}'`],'style-src': ["'self'", `'nonce-${emotionalNonce}'`, `'nonce-${emotionalNonce3}'`]},{enabled: true,hashingMethod: 'sha256',hashEnabled: {'script-src': true,'style-src': true,'default-src': true},nonceEnabled: {'script-src': true,'style-src': true,'default-src': true}})],
})module.exports = webpackConfig
生产打包
配置完毕,开始打包。如果过程顺利,你会发现,页面的js脚本的引入,会增加一串nonce值。同时,HTML meta标签下也新增了Content-Security-Policy
头:
<meta http-equiv="Content-Security-Policy" content="base-uri 'self'; object-src 'none'; script-src http://192.168.1.1:8080 'nonce-xxxxxxxxxxxxxxxxxxxxxxx==' 'sha256-xxxxxxxxxx=' 'nonce-xxxxxxxxxxxxxxxxxxxxxxx' 'nonce-xxxxxxxxxxxxxxxxxxxxxxx' 'nonce-xxxxxxxxxxxxxxxxxxxxxxx' 'nonce-xxxxxxxxxxxxxxxxxxxxxxx' 'nonce-xxxxxxxxxxxxxxxxxxxxxxx' 'nonce-xxxxxxxxxxxxxxxxxxxxxxx' 'nonce-xxxxxxxxxxxxxxxxxxxxxxx' 'nonce-xxxxxxxxxxxxxxxxxxxxxxx' 'nonce-xxxxxxxxxxxxxxxxxxxxxxx'; style-src http://192.168.1.1:8080 'nonce-xxxxxxxxxxxxxxxxxxxxxxx==' 'nonce-xxxxxxxxxxxxxxxxxxxxxxx==' 'nonce-xxxxxxxxxxxxxxxxxxxxxxx==' 'nonce-xxxxxxxxxxxxxxxxxxxxxxx==' 'nonce-xxxxxxxxxxxxxxxxxxxxxxx==' 'nonce-xxxxxxxxxxxxxxxxxxxxxxx=='">
这里单独解释下,按理来说,插件已经为我们设置了http-equiv
,也就是浏览器在解析时,会把这部分视为头部进行对待
当浏览器加载包含 http-equiv 属性的 标签的 HTML 页面时,它会在解析该页面时读取 标签的内容,并将这些内容视为相应的 HTTP 头部
但对于安全检查软件来说,这是远远不够的,它把响应头Content-Security-Policy
视为检查的一个case,因此,必须要设置Nginx,这也是这个安全问题的繁琐所在。
配置Nginx
将index.html
下生成的nonce取出,设置于Content-Security-Policy
location / {add_header Content-Security-Policy "Frame-ancestors 'none'; img-src 'self' data:;base-uri 'self'; object-src 'none'; default-src http://192.168.1.1:8080; script-src http://192.168.1.1:8080 'nonce-xxxxxxxxxxxxxxxxxxxxxxx==' 'sha256-xxxxxxxxxx=' 'nonce-xxxxxxxxxxxxxxxxxxxxxxx' 'nonce-xxxxxxxxxxxxxxxxxxxxxxx' 'nonce-xxxxxxxxxxxxxxxxxxxxxxx' 'nonce-xxxxxxxxxxxxxxxxxxxxxxx' 'nonce-xxxxxxxxxxxxxxxxxxxxxxx' 'nonce-xxxxxxxxxxxxxxxxxxxxxxx' 'nonce-xxxxxxxxxxxxxxxxxxxxxxx' 'nonce-xxxxxxxxxxxxxxxxxxxxxxx' 'nonce-xxxxxxxxxxxxxxxxxxxxxxx'; style-src http://192.168.1.1:8080 'nonce-xxxxxxxxxxxxxxxxxxxxxxx==' 'nonce-xxxxxxxxxxxxxxxxxxxxxxx==' 'nonce-xxxxxxxxxxxxxxxxxxxxxxx==' 'nonce-xxxxxxxxxxxxxxxxxxxxxxx==' 'nonce-xxxxxxxxxxxxxxxxxxxxxxx==' 'nonce-xxxxxxxxxxxxxxxxxxxxxxx==';font-src http://192.168.1.1:8080 data:";
}
关于svg
需要注意的几个点是:
- 去除svg文件内含的
style
标签。csp-html-webpack-plugin
目前看,其并不能处理到svg下style这一层级(至少目前没有找到)
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="200" height="200"><defs><style/></defs>
2、修改svg-sprite-loader
查过svg-sprite-loader
代码,其对svg的处理,有这么一段代码:
var defaultConfig = {attrs: ( obj = {style: ['position: absolute', 'width: 0', 'height: 0'].join('; '),'aria-hidden': 'true'}, obj[svg$1.name] = svg$1.uri, obj[xlink$1.name] = xlink$1.uri, obj )
};
与1同理,这里无法对其设置nonce
值,最终也会被CSP规则拦截。目前我采取的办法是清理掉代码下此类style标签,重新安装npm包。如果你用其他的loader,也可以把此作为检查点
页面检查
初次配置完毕后,还有很大概率页面无法访问或者安全检查不通过,有如下几个原因可供排查
- Nginx在复制页面的Nonce导致两处不匹配;
- 缺少其他的属性配置,如
font-src
等;
6、Missing or insecure “X-Content-Type-Options” header
此响应头的解释:
The X-Content-Type-Options response HTTP header is a marker used by the server to indicate that the MIME types advertised in the Content-Type headers should be followed and not be changed. The header allows you to avoid MIME type sniffing by saying that the MIME types are deliberately configured.
浏览器进行 MIME 类型推断的原因主要是为了在缺少或错误的 Content-Type 头部信息时,仍然能够合理呈现和处理内容。但这种推断行为可能带来安全风险。典型的就是跨站点脚本攻击,如果把一个携带危险脚本的响应内容错误的推断为了JavaScript,岂不是很危险。
解决方式也很简单
location / {add_header X-Content-Type-Options "nosniff";
}
此指令的含义是:
Blocks a request if the request destination is of type style and the MIME type is not text/css, or of type script and the MIME type is not a JavaScript MIME type.
7、Missing or insecure HTTP Strict-Transport-Security Header
此响应头解释:
The HTTP Strict-Transport-Security response header (often abbreviated as HSTS) informs browsers that the site should only be accessed using HTTPS, and that any future attempts to access it using HTTP should automatically be converted to HTTPS.
此响应头在服务开启https
才生效
location / {add_header Strict-Transport-Security "max-age=31536000";
}
8、Clickjacking through X-Frame-Option Header
Clickjacking
在此意思大致是,攻击者搞了一个透明Iframe
,然后把原网站嵌进来。Iframe做了下样式修饰,看上去和源网站别无二致,然后加了点危险的按钮点击,别人一点完蛋。
处理方式也很简单,设置不允许iframe
嵌套即可
server {listen 80;server_name example.com;# 防止嵌入到 iframeadd_header X-Frame-Options "DENY";location / {# 其他配置}
}
参考
什么是跨站脚本攻击(XSS)
Next.js