前端安全汇总(持续更新)
在移动互联网时代,信息安全越来越重要。前端方面也面临着越来越多的安全挑战,本文收集了网上各位大佬的分析总结,将持续整理总结常见的安全问题及其防御措施。
CSRF
CSRF 概念
CSRF
(Cross-site request forgery)跨站请求伪造:攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻击网站发送跨站请求。利用受害者在被攻击网站已经获取的注册凭证(cookies
等),绕过后台的用户验证,达到冒充用户对被攻击的网站执行某项操作的目的。
CSRF 攻击流程
- 受害者登录
a.com
,并保留了登录凭证(Cookie
)。 - 攻击者引诱受害者访问了
b.com
。 b.com
向a.com
发送了一个请求:a.com/act=xx
。浏览器会默认携带a.com
的Cookie
。a.com
接收到请求后,对请求进行验证,并确认是受害者的凭证,误以为是受害者自己发送的请求。a.com
以受害者的名义执行了act=xx
。- 攻击完成,攻击者在受害者不知情的情况下,冒充受害者,让
a.com
执行了自己定义的操作。
CSRF 攻击类型
GET
GET
类型的 CSRF
利用非常简单,只需要一个 HTTP
请求,一般会这样利用:
<img src="http://bank.example/withdraw?amount=10000&for=hacker" />
在受害者访问含有这个 img
的页面后,浏览器会自动向 http://bank.example/withdraw?account=xiaoming&amount=10000&for=hacker
发出一次 HTTP
请求。bank.example
就会收到包含受害者登录信息的一次跨域请求。
POST
这种类型的 CSRF
利用起来通常使用的是一个自动提交的表单,如:
<form action="http://bank.example/withdraw" method="POST">
<input type="hidden" name="account" value="xiaoming" />
<input type="hidden" name="amount" value="10000" />
<input type="hidden" name="for" value="hacker" />
</form>
<script>
document.forms[0].submit();
</script>
访问该页面后,表单会自动提交,相当于模拟用户完成了一次 POST
操作。 POST
类型的攻击通常比 GET
要求更加严格一点,但仍并不复杂。任何个人网站、博客,被黑客上传页面的网站都有可能是发起攻击的来源,后端接口不能将安全寄托在仅允许 POST
上面。
链接
链接类型的 CSRF
并不常见,比起其他两种用户打开页面就中招的情况,这种需要用户点击链接才会触发。这种类型通常是在论坛中发布的图片中嵌入恶意链接,或者以广告的形式诱导用户中招,攻击者通常会以比较夸张的词语诱骗用户点击,例如:
<a href="http://test.com/csrf/withdraw.php?amount=1000&for=hacker" taget="_blank">
重磅消息!!
<a
/></a>
由于之前用户登录了信任的网站 A
,并且保存登录状态,只要用户主动访问上面的这个 PHP
页面,则表示攻击成功。
CSRF 特性
- 攻击一般发起在第三方网站,而不是被攻击的网站。被攻击的网站无法防止攻击发生。
- 攻击利用受害者在被攻击网站的登录凭证,冒充受害者提交操作;而不是直接窃取数据。
- 整个过程攻击者并不能获取到受害者的登录凭证,仅仅是“冒用”。
- 跨站请求可以用各种方式:
图片 URL
、超链接
、CORS
、Form 提交
等等。部分请求方式可以直接嵌入在第三方论坛、文章中,难以进行追踪。
CSRF 防御措施
CSRF
通常从第三方网站发起,被攻击的网站无法防止攻击发生,只能通过增强自己网站针对 CSRF
的防护能力来提升安全性。
同源检测
服务器可通过 request headers
里 origin
和 referer
两个字段确定请求的来源域。
如果攻击者设置 Referrer Policy
隐藏了 referer
,则建议直接阻止(考虑排除搜索引擎搜索的情况)。
cookie 是不能跨域访问的,为什么还会有 csrf? 浏览器会依据加载的域名附带上对应域名 cookie。如用户在 a 网站登录且生成了授权的 cookies,然后访问 b 网站,b 站故意构造请求 a 站的请求,如删除操作之类的,用不受同源影响的 script,img 或者 iframe 之类的标签加载 a 地址,浏览器会附带上 a 站此登录用户的授权 cookie 信息,这样就构成 crsf,会删除掉当前用户的数据。
Token
而 CSRF
攻击之所以能够成功,是因为服务器误把攻击者发送的请求当成了用户自己的请求。那么我们可以要求所有的用户请求都携带一个 CSRF
攻击者无法获取到的 Token
。服务器通过校验请求是否携带正确的 Token
,来把正常的请求和攻击的请求区分开,也可以防范 CSRF
的攻击。
- 将
Token
输出到页面中,对于页面整个DOM
树中所有的a
和form
标签后都加入Token
。 - 页面提交的请求携带这个
Token
- 服务器验证
Token
是否正确
验证码&密码
在关键请求时要求再次输入验证码和密码之类的,打断 csrf
的进程,简单粗暴且有效。
双重 Cookie 验证
利用 CSRF
攻击不能获取到用户 Cookie
的特点,我们可以要求 Ajax
和表单请求携带一个 Cookie
中的值。
- 在用户访问网站页面时,向请求域名注入一个
Cookie
,内容为随机字符串(例如csrfcookie=v8g9e4ksfhw
)。 - 在前端向后端发起请求时,取出
Cookie
,并添加到URL
的参数中(接上例POST
https://www.a.com/comment?csrfcookie=v8g9e4ksfhw
)。 - 后端接口验证
Cookie
中的字段与URL
参数中的字段是否一致,不一致则拒绝。
Samesite 属性
Chrome 51 开始,浏览器的 Cookie
新增加了一个 SameSite
属性限制第三方 Cookie
,用来防止 CSRF
攻击和用户追踪。
SameSite
根据严格程度取值为 Strict
, Lax
, None
。
目前兼容性及实用性都不太好(不支持子域),暂时不用。
XSS
XSS 概念
Cross-Site Scripting
(跨站脚本攻击)简称 XSS
,是一种代码注入攻击。攻击者通过在目标网站上注入恶意脚本,使之在用户的浏览器上运行。利用这些恶意脚本,攻击者可获取用户的敏感信息如 Cookie
、SessionID
等,进而危害数据安全。
XSS 分类
类型 | 存储区 | 插入点 |
---|---|---|
存储型 XSS | 后端数据库 | HTML |
反射型 XSS | URL | HTML |
DOM 型 XSS | 后端数据库/前端存储/URL | 前端 JavaScript |
存储型 XSS
- 攻击者将恶意代码提交到目标网站的数据库中。
- 用户打开目标网站时,网站服务端将恶意代码从数据库取出,拼接在
HTML
中返回给浏览器。 - 用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。
- 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。
这种攻击常见于带有用户保存数据的网站功能,如论坛发帖、商品评论、用户私信等。
反射型 XSS
- 攻击者构造出特殊的
URL
,其中包含恶意代码。 - 用户打开带有恶意代码的
URL
时,网站服务端将恶意代码从URL
中取出,拼接在 HTML 中返回给浏览器。 - 用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。
- 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。
反射型 XSS
漏洞常见于通过 URL
传递参数的功能,如网站搜索、跳转等。由于需要用户主动打开恶意的 URL
才能生效,攻击者往往会结合多种手段诱导用户点击。
DOM 型 XSS
- 攻击者构造出特殊的
URL
,其中包含恶意代码。 - 用户打开带有恶意代码的
URL
。 - 用户浏览器接收到响应后解析执行,前端
JavaScript
取出URL
中的恶意代码并执行。 - 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。
DOM
型 XSS
跟前两种 XSS
的区别:DOM
型 XSS
攻击中,取出和执行恶意代码由浏览器端完成,属于前端 JavaScript
自身的安全漏洞,而其他两种 XSS
都属于服务端的安全漏洞。
XSS 防御
针对攻击者提交恶意代码
输入过滤
对前端而言,输入过滤不可靠。因为一旦攻击者绕过前端过滤,直接构造请求,就可以提交恶意代码了。
输入内容长度控制
对于不受信任的输入,都应该限定一个合理的长度。虽然无法完全防止 XSS
发生,但可以增加 XSS
攻击的难度。
验证码
防止脚本冒充用户提交危险操作。
针对浏览器执行恶意代码
纯前端渲染
在纯前端渲染中,我们会明确的告诉浏览器:下面要设置的内容是文本(.innerText
),还是属性(.setAttribute
),还是样式(.style
)等等。浏览器不会被轻易的被欺骗,执行预期外的代码了。
转义 HTML
应当尽量避免转义 HTML
。但如果不是纯前端渲染,就需要采用合适的转义库,对 HTML
模板各处插入点进行充分的转义。
预防 DOM 型 XSS 攻击
防范存储型和反射型 XSS
是后端 RD
的责任。而 DOM
型 XSS
攻击不发生在后端。DOM
型 XSS
攻击,实际上就是网站前端 JavaScript
代码本身不够严谨,把不可信的数据当作代码执行了。
在使用 .innerHTML
、.outerHTML
、document.write()
时要特别小心,不要把不可信的数据作为 HTML
插到页面上,而应尽量使用 .textContent
、.setAttribute()
等。
如果用 Vue/React
技术栈,并且不使用 v-html/dangerouslySetInnerHTML
功能,就在前端 render
阶段避免 innerHTML、outerHTML
的 XSS
隐患。
DOM
中的内联事件监听器,如 location、onclick、onerror、onload、onmouseover
等,<a>
标签的 href
属性,JavaScript
的 eval()、setTimeout()、setInterval()
等,都能把字符串作为代码运行。如果不可信的数据拼接到字符串中传递给这些 API
,很容易产生安全隐患,请务必避免。
CSP
内容安全策略 (CSP
) 是一个额外的安全层,用于检测并削弱某些特定类型的攻击,包括跨站脚本 (XSS
) 和数据注入攻击等。
通过 HTTP Header
来定义(优先):
"Content-Security-Policy:" 策略集
通过 html meta
标签使用:
<meta http-equiv="content-security-policy" content="策略集" />
更多策略
- 禁止加载外域代码,防止复杂的攻击逻辑。
- 禁止外域提交,网站被攻击后,用户的数据不会泄露到外域。
- 禁止内联脚本执行(规则较严格,目前发现 GitHub 使用)。
- 禁止未授权的脚本执行(新特性,Google Map 移动版在使用)。
- 合理使用上报可以及时发现 XSS,利于尽快修复问题。
HttpOnly & Secure
cookie
中设置了HttpOnly
属性,禁止JavaScript
读取某些敏感Cookie
,攻击者完成XSS
注入后也无法窃取此Cookie
。cookie
中设置了Secure
属性,规定cookie
只能在https
协议下才能够发送到服务器。防止信息在传递的过程中被监听捕获后信息泄漏。
iframe 嵌套
点击劫持 (ClickJacking
) 一般会利用透明 iframe
覆盖原网页诱导用户进行某些操作达成目的。
防止其他页面通过 iframe 引用
//自己网站添加
if (top.location != self.location) {
top.location.href = 'http://www.baidu.com'; //若被其他网站引用则强制跳转
}
X-FRAME-OPTIONS
或者添加 HTTP
响应头: X-FRAME-OPTIONS
HTTP
响应头信息中的 X-Frame-Options
,可以指示浏览器是否应该加载一个 iframe
中的页面。如果服务器响应头信息中没有 X-Frame-Options
,则该网站存在 ClickJacking
攻击风险。
防止引用的其他 iframe 篡改自己的页面
添加 sandbox 属性
<iframe src="其他页面.html" frameborder="0" sandbox=""></iframe>
opener
通过以下两种方式打开的页面可以使用 window.opener
来访问源页面的 window
对象,进而进行篡改原页面:
window.open
window.open('http://www.baidu.com');
防御方式:
let newTab = window.open();
newTab.opener = null;
newTab.location = url;
a 标签加 _blank
<a target="_blank" href="http://www.baidu.com"></a>
防御方式:
<a target="_blank" href="http://www.baidu.com" rel="noopener noreferrer"></a>
CDN 劫持
出于性能考虑,前端应用通常会把一些静态资源存放到 CDN
(Content Delivery Networks)上面,例如 js
脚本和 style
文件。这么做可以显著提高前端应用的访问速度,但与此同时却也隐含了一个新的安全风险。如果攻击者劫持了 CDN
,或者对 CDN
中的资源进行了污染,攻击者可以肆意篡改我们的前端页面,对用户实施攻击。
SRI
SRI 简介
子资源完整性(SRI
)是允许浏览器检查其获得的资源(例如从 CDN
获得的)是否被篡改的一项安全特性。它通过验证获取文件的哈希值是否和你提供的哈希值一样来判断资源是否被篡改。
通过给 link
标签或者 script
标签增加 integrity
属性即可开启 SRI
功能.
<script type="text/javascript" src="//s.url.cn/xxxx/aaa.js" integrity="sha256-xxx sha384-yyy" crossorigin="anonymous"></script>
integrity
值分成两个部分,第一部分指定哈希值的生成算法(sha256
、sha384
及 sha512
),第二部分是经过 base64
编码的实际哈希值,两者之间通过一个短横(-
)分割。integrity
值可以包含多个由空格分隔的哈希值,只要文件匹配其中任意一个哈希值,就可以通过校验并加载该资源。
crossorigin="anonymous" 的作用是引入跨域脚本,使用 SRI
要保证资源同域或开启跨域。如果不加此属性则表示不开启 CORS 策略。
浏览器处理 SRI 原理
- 当浏览器在
script
或者link
标签中遇到integrity
属性之后,会在执行脚本或者应用样式表之前对比所加载文件的哈希值和期望的哈希值。 - 当脚本或者样式表的哈希值和期望的不一致时,浏览器必须拒绝执行脚本或者应用样式表,并且必须返回一个网络错误说明获得脚本或样式表失败。
如何使用 SRI
通过使用 webpack
的 html-webpack-plugin
和 webpack-subresource-integrity
可以生成包含 integrity
属性 script
标签。
import SriPlugin from 'webpack-subresource-integrity';
const compiler = webpack({
output: {
crossOriginLoading: 'anonymous',
},
plugins: [
new SriPlugin({
hashFuncNames: ['sha256', 'sha384'],
enabled: process.env.NODE_ENV === 'production',
}),
],
});
然后通过插件 script-ext-html-webpack-plugin
在 script
里注入 onerror
事件和 onsuccess
事件。onerror
事件里再次请求一次数据,比较两次数据是否一致来判断是否被 CDN
劫持。
联合 CSP
另外,通过在 CSP
头部添加:
Content-Security-Policy: require-sri-for script;
这条指令规定了所有 JavaScript
都要有 integrity
属性,且通过验证才能被加载。
中间人攻击
攻击原理
用户在访问某个网站的时候,在浏览器里却往往直接输入网站域名。浏览器便向网站发起一次 HTTP
请求,在得到一个重定向响应后,再发起一次 HTTPS
请求并得到最终的响应内容。由于在建立起 HTTPS
连接之前存在一次明文的 HTTP
请求和重定向,使得攻击者可以以中间人的方式劫持这次请求,从而进行后续的攻击,例如窃听数据,篡改请求和响应,跳转到钓鱼网站等。
HSTS
HSTS
的全称是 HTTP
Strict-Transport-Security
(严格传输安全),它是一个 Web
安全策略机制,通过服务器设置 HTTP Response Header
告诉浏览器只能通过 HTTPS
访问当前资源,而不是 HTTP
。
HSTS 语法
Strict-Transport-Security: <max-age=>[; includeSubDomains][; preload]
max-age
是必选参数,是一个以秒为单位的数值,它代表着HSTS Header
的过期时间,通常设置为 1 年,即 31536000 秒。includeSubDomains
是可选参数,如果包含它,则意味着当前域名及其子域名均开启HSTS
保护。preload
是可选参数,只有当你申请将自己的域名加入到浏览器内置列表的时候才需要使用到它
HSTS 更多
- 只要是在有效期内,浏览器都将直接强制性的发起
HTTPS
请求。 HSTS
让浏览器强制拒绝不安全的链接,不给用户选择的机会。- 第一次访问网站的时候,依然需要一次明文的
HTTP
请求和重定向才能切换到HTTPS
,以及刷新HSTS
信息,此时仍然可以进行中间人攻击,对此,浏览器里内置一个列表Preload List
,只要是在这个列表里的域名,无论何时、何种情况,浏览器都只使用HTTPS
发起连接。
cookie secure
通过 cookie
secure
保证你的 session cookie
对于攻击者是不可见的,避免中间人攻击。
其他
HTTPS
HTTPS=HTTP+SSL
确保所有需要的资源都用 HTTPS
引入.
SSL/TLS
传输层安全协议(Transport Layer Security / TLS
)及其前身安全套接层(Secure Sockets Layer / SSL
)为浏览器和服务器提供了端到端的加密手段,为互联网通信提供了安全可靠性保障。没有 TLS
,其他安全手段不堪一击。TLS
是 HTTP
安全的基石。
Cache Control
指定页面的缓存策略。强烈建议手动指定页面缓存策略,否则会由浏览器和代理来控制是否缓存内容。一个不当的缓存策略可能会导致性能问题和安全问题。
Content Type Options
通常浏览器根据响应头的 Content Type
字段分辨资源类型,但假若某些资源 Content Type
是错的或未定义,浏览器会启用 MIME-sniffing
来猜测该资源的类型,解析内容并执行。
X-Content-Type-Options
响应头可以关闭浏览器的资源 MIME-sniffing
功能:
X-Content-Type-Options: nosniff
信息泄露
以下都应删除
Server Banner
响应头里加入Server Banner
来标明自己身份和版本号 如:server:nginx/1.10.0 (Ubuntu)
Web
框架信息 很多Web
框架都会设置响应头来标示自己身份、版本。这些响应头都是非标准的,而且也不影响页面渲染,只是为了框架自身的宣传。如:X-Powered-By, X-Runtime, X-Version, X-AspNet-Version
等