Web 安全之 XSS 和 CSRF
伴随着前端技术的不断发展,Web
应用已经随处可见了。随之而来的问题就是,谁来保护用户的隐私和网络安全?
一、网络安全
网络安全是指网络系统的硬件、软件及其系统中的数据受到保护,不因偶然的或者恶意的原因而遭受到破坏、更改、泄露,系统连续可靠正常地运行,网络服务不中断。
网络安全主要有如下特征:
- 保密性:信息不泄露给非授权用户、实体或过程,或供其利用的特性。
- 完整性:数据未经授权不能进行改变的特性。即信息在存储或传输过程中保持不被修改、不被破坏和丢失的特性。
- 可用性:可被授权实体访问并按需求使用的特性。即当需要时能否存取所需的信息。例如网络环境下拒绝服务、破坏网络和有关系统的正常运行等都属于对可用性的攻击;
- 可控性:对信息的传播及内容具有控制能力。
- 可审查性:出现安全问题时提供依据与手段。
通过上面,我们也了解到网络安全其实与我们息息相关,而作为 Web
前端开发工程师,这方面知识一定要了解,可能我们是距离用户最近的开发者。所以我们先从 Web
最常见的 XSS
和 CSRF
攻击来入手吧。
这个时代软件的特点就是:解决了用户痛点,就能为王!
二、什么是 XSS ?
跨站脚本攻击(Cross Site Scripting
,为了不和层叠样式表 CSS
混淆,故将跨站脚本攻击缩写为 XSS
)。恶意攻击者往 Web
页面里插入恶意 Script
代码,当用户浏览该页之时,嵌入其中 Web
里面的 Script
代码会被执行,从而达到恶意攻击用户的目的。
XSS
主要有如下三种分类:
Reflected XSS
(基于反射的XSS
攻击):主要通过利用系统反馈行为漏洞,并欺骗用户主动触发,从而发起Web
攻击。Stored XSS
(基于存储的XSS
攻击):Stored XSS
和Reflected XSS
的差别就在于,具有攻击性的脚本被保存到了服务器并且可以被普通用户完整的从服务的取得并执行,从而获得了在网络上传播的能力。DOM-based or local XSS
(基于DOM
或本地的XSS
攻击):DOM
型XSS
其实是一种特殊类型的反射型XSS
,它是基于DOM
文档对象模型的一种漏洞。可以通过DOM
来动态修改页面内容,从客户端获取DOM
中的数据并在本地执行。基于这个特性,就可以利用JS
脚本来实现XSS
漏洞的利用。
XSS
是一门又热门又不太受重视的 Web
攻击方式,为什么会这样呢,原因如下:
- 耗时间;
- 有一定几率不成功;
- 没有相应的软件来完成自动化攻击;
- 前期需要基本的
HTML
、JS
功底,后期需要扎实的HTML
、JS
、ActionScript2/3.0
等语言的功底; - 是一种被动的攻击手法;
- 对
Website
有http-only
、crossdomian.xml
没有用。
但是这些并没有影响黑客对此漏洞的偏爱,原因只有一个:XSS
几乎每个网站都存在。
三、XSS 情景再现
为了更好的说明 XSS
,我们来举一些小栗子:
1.在网页 input
或者 textarea
中输入 <script>alert('xss')</script>
,如果弹出 alert
对话框,则说明网站存在 XSS
漏洞。
请注意:我们经常在一些博客网站或者论坛底下的留言框看到 alert
,你以为是用户热情,其实他们是想搞你!
2.只要网页存在 XSS
漏洞,攻击者可能会嵌入 JS
脚本,如:1
<script scr="jartto_xss_js"></script> // 获取 cookie ,发送用户信息等
3.图片也可能会被利用,如:1
<img src="..." onerror="appendChild(createElement('script')).src='jartto_xss_js'" />
4.直接使用 URL
参数攻击,如:1
https://www.baidu.com?jarttoTest=<script>alert(document.cookie)</script>
当然,这里需要注意的是,URL
也有可能是不知来历的二维码,所以请慎重扫码。
到这里你可能就了解了,XSS
的目的就是在目标页面执行不为人知的 JS 脚本,从而窥探用户隐私甚至财产。
四、XSS 漏洞检测以及防御
通常有一些方式可以测试网站是否有正确处理特殊字符,具体如下:1
2
3
4
5
6
7
8
9
10><script>alert(document.cookie)</script>
='><script>alert(document.cookie)</script>
"><script>alert(document.cookie)</script>
<script>alert(document.cookie)</script>
<script>alert(vulnerable)</script>
%3Cscript%3Ealert('XSS')%3C/script%3E
<script>alert('XSS')</script>
<img src="javascript:alert('XSS')">
<img src="http://jartto.com/test.png" onerror="alert('XSS')">
<div style="height:expression(alert('XSS'),1)" />(这个仅限 IE 有效)
防御手段可以采取如下方式:
- 对所有用户提交内容进行可靠的输入验证,包括对
URL
、查询关键字、HTTP
头、POST
数据等,仅接受指定长度范围内、采用适当格式、采用所预期的字符的内容提交,对其他的一律过滤。 - 使用
HTTP
头指定类型,很多时候可以使用HTTP
头指定内容的类型,使得输出的内容避免被作为HTML
解析。 - 实现
Session
标记(session tokens)、CAPTCHA
系统或者HTTP
引用头检查,以防功能被第三方网站所执行。 - 确认接收的的内容被妥善的规范化,仅包含最小的、安全的
Tag
(没有JS
),去掉任何对远程内容的引用(尤其是样式表和JS
),使用HTTP only
的Cookie
。
避免 XSS
的方法之一主要是将用户所提供的内容进行过滤,许多语言都有提供对 HTML
的过滤,看似很简单,实则很有效。
五、什么是 CSRF ?
跨站请求伪造 CSRF
(Cross-site request forgery),也被称为 One Click Attack
或者 Session Riding
,通常缩写为 CSRF
或者 XSRF
,是一种对网站的恶意利用。尽管听起来像跨站脚本 XSS
,但它与 XSS
非常不同,XSS
利用站点内的信任用户,而 CSRF
则通过伪装来自受信任用户的请求来利用受信任的网站。
与 XSS
攻击相比,CSRF
攻击往往不大流行(因此对其进行防范的资源也相当稀少)和难以防范,所以被认为比 XSS
更具危险性。
要完成一次 CSRF
攻击,受害者必须依次完成两个步骤:
- 登录受信任网站
A
,并在本地生成Cookie
。 - 在不登出
A
的情况下,访问危险网站B
。
那么这里有个问题:如果我不满足以上两个条件中的一个,我就不会受到 CSRF
的攻击”。是的,确实如此,但你不能保证以下情况不会发生:
- 你不能保证你登录了一个网站后,不再打开一个
tab
页面并访问另外的网站。 - 你不能保证你关闭浏览器了后,你本地的
Cookie
立刻过期,你上次的会话已经结束。 - 攻击网站,可能是一个存在其他漏洞的可信任的经常被人访问的网站。
六、CSRF 情景再现
为了阐述 CSRF
的操作流程,我们来举几个不太恰当,但足够说明问题的栗子:
示例一:银行网站 A
,它以 GET
请求来完成银行转账的操作,如:1
http://www.mybank.com/Transfer.php?toBankId=11&money=1000
危险网站 B
,它里面有一段 HTML
的代码如下:1
<img src=http://www.mybank.com/Transfer.php?toBankId=11&money=1000>
首先,你登录了银行网站 A ,然后访问危险网站 B ,噢,这时你会发现你的银行账户少了 1000 块…
究竟发生了什么,1000 块去哪里了?
- 银行网站
A
违反了HTTP
规范,使用GET
请求更新资源。 - 在访问危险网站
B
的之前,你已经登录了银行网站A
,而B
中的以GET
的方式请求第三方资源(这里的第三方就是指银行网站了,原本这是一个合法的请求,但这里被不法分子利用了)。 - 此时你的浏览器会带上你的银行网站
A
的Cookie
发出Get
请求,去获取资源。 - 银行网站服务器收到请求后,认为这是一个更新资源操作(转账操作),所以就立刻进行转账操作。
示例二:为了杜绝上面的问题,银行决定改用 POST
请求完成转账操作。银行网站 A
的 WEB
表单如下:1
2
3
4
5<form action="Transfer.php" method="POST">
<p>ToBankId: <input type="text" name="toBankId"/></p>
<p>Money: <input type="text" name="money"/></p>
<p><input type="submit" value="Transfer"/></p>
</form>
后台处理页面 Transfer.php
如下:1
2
3
4
5
6
7
session_start();
if (isset($_REQUEST['toBankId'] && isset($_REQUEST['money']))
{
buy_stocks($_REQUEST['toBankId'], $_REQUEST['money']);
}
危险网站 B
,仍然只是包含那句 HTML
代码:1
<img src=http://www.mybank.com/Transfer.php?toBankId=11&money=1000>
和示例一中的操作一样,你首先登录了银行网站 A
,然后访问危险网站 B
,结果和示例一一样,你再次没了 1000块😱。
我们来捋一捋,1000 块又是怎么丢的?
- 银行后台使用了
$_REQUEST
去获取请求的数据,而$_REQUEST
既可以获取GET
请求的数据,也可以获取POST
请求的数据,这就造成了在后台处理程序无法区分这到底是GET
请求的数据还是POST
请求的数据。
在 PHP
中,可以使用 $_GET
和 $_POST
分别获取 GET
请求和 POST
请求的数据。
示例三:经过前面两个惨痛的教训,银行决定把获取请求数据的方法也改了,改用 $_POST
,只获取 POST
请求的数据,后台处理页面 Transfer.php
代码如下:1
2
3
4
5
6
7
session_start();
if (isset($_POST['toBankId'] && isset($_POST['money']))
{
buy_stocks($_POST['toBankId'], $_POST['money']);
}
然而,危险网站 B
与时俱进,它改了一下代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18<html>
<head>
<script type="text/javascript">
function steal() {
iframe = document.frames["steal"];
iframe.document.Submit("transfer");
}
</script>
</head>
<body onload="steal()">
<iframe name="steal" display="none">
<form action="http://www.myBank.com/Transfer.php" method="POST">
<input type="hidden" name="toBankId" value="11"></input>
<input type="hidden" name="money" value="1000"></input>
</form>
</iframe>
</body>
</html>
如果用户仍是继续上面的操作,很不幸,结果将会是再次不见 1000 块,因为这里危险网站 B
暗地里发送了 POST
请求到银行!
在损失 3000 块之后,我们意识到了 CSRF
攻击的严重性,来总结一下:CSRF
主要的攻击模式基本上是以上的 3 种,其中以第 1, 2 种最为严重,因为触发条件很简单,一个就可以了,而第3种比较麻烦,需要使用 JS
,所以使用的机会会比前面的少很多,但无论是哪种情况,只要触发了 CSRF
攻击,后果都有可能很严重。
CSRF
攻击是源于 WEB
的隐式身份验证机制,而 WEB
的身份验证机制虽然可以保证一个请求是来自于某个用户的浏览器,但却无法保证该请求是用户批准发送的!
此处示例出自Web 安全的三个攻防姿势,查看详情请移步。还有一个有意思的栗子,出自 developerWorks
。
七、CSRF 漏洞如何防御?
1.验证 HTTP Referer
字段,利用 HTTP
头中的 Referer
判断请求来源是否合法。
- 优点:简单易行,只需要在最后给所有安全敏感的请求统一增加一个拦截器来检查
Referer
的值就可以。特别是对于当前现有的系统,不需要改变当前系统的任何已有代码和逻辑,没有风险,非常便捷。 - 缺点:
Referer
的值是由浏览器提供的,不可全信,低版本浏览器下Referer
存在伪造风险。- 用户自己可以设置浏览器使其在发送请求时不再提供
Referer
时,网站将拒绝合法用户的访问。
2.在请求地址中添加 token
并验证,在请求中放入黑客所不能伪造的信息,并且该信息不存在于 cookie
之中,以HTTP
请求参数的形式加入一个随机产生的 token
交由服务端验证。
- 优点:比检查
Referer
要安全一些,并且不涉及用户隐私。 - 缺点:对所有请求都添加
token
比较困难,难以保证token
本身的安全,依然会被利用获取到token
。
3.在 HTTP
头中自定义属性并验证 One-Time Tokens
,将 token
放到 HTTP
头中自定义的属性里。通过 XMLHttpRequest
的异步请求交由后端校验,并且一次有效。
- 优点:统一管理
token
输入输出,可以保证token
的安全性。 - 缺点:有局限性,无法在非异步的请求上实施。
虽然上面的这些方案可以很大程度上抵御 CSRF
的攻击,但并没有一种完美的解决方案。由于这些新的方案尚不成熟,要正式投入使用并被业界广为接受还需时日。
在这之前,我们只有充分重视 CSRF
,根据系统的实际情况选择最合适的策略,这样才能把 CSRF
的危害降到最低。
八、总结
随着 Web2.0
、社交网络、微博等等一系列新型的互联网产品的诞生,基于 Web
环境的互联网应用越来越广泛,企业信息化的过程中各种应用都架设在 Web
平台上,Web
业务的迅速发展也引起黑客们的强烈关注,接踵而至的就是 Web
安全威胁的凸显。
在时代赋予 Web
前端开发使命的时候,我们应该比其他人更谨慎,更懂得应付危害网络安全的行为,任重而道远啊~