@garygchai
2016-05-07T19:57:53.000000Z
字数 4897
阅读 3964
XSS
很早就想写一篇关于跨站脚本攻击的文章(XSS),因为之前看到个别组员毫不犹豫地使用innerHTML这样的方式输出用户输入的内容,我们网站本身也发生过几次xss注入的安全事故,这些事情也暴露了我们整个团队的安全意识不够吧。这几天正好看了一些关于xss的文章,也算做一些笔记吧。
跨站脚本攻击,英文全称是Cross Site Script,按理说缩写应该是CSS吧,据说是为了和CSS(Cascading Style Sheet)有所区别,才叫XSS的!XSS攻击,通常是指黑客通过html注入篡改了网页,插入了恶意的脚本,从而在用户浏览器网页的时候,控制用户浏览器的一种攻击。
XSS通常都分为3类:反射型XSS、存储型XSS、Dom Based XSS,但是不止,还有各种变种,我还听过mXSS,UXSS等等。
<!DOCTYPE html>
<html>
<head>
<title>反射型XSS攻击</title>
<meta charset="utf-8"/>
</head>
<body>
<h1>
Hello <span id="value"></span>
</h1>
<script type="text/javascript">
var hash = location.hash.slice(1);
if (hash) {
document.getElementById('value').innerHTML = hash;
}
</script>
</body>
</html>
此时,我们用innerHTML输出链接的内容就让攻击者有了可乘之机,攻击者通过构造的这样的url:http://localhost:3000/index.html#<img src="1" onerror="alert(document.cookie)">
,发给用户用户点击链接,就可以做到XSS攻击。
存储型XSS
存储型XSS会攻击者的数据存储在服务器端,攻击行为也将伴随着攻击数据一直存在,所以这种攻击类型也叫持久型XSS。我记得我们网站早期的时候,家族留言区发生过这种攻击漏洞,用户输入类似<script>alert(document.cookie)</script>
的留言存入我们的数据库中,前端显示的时候完完全全输出了用户输入的脚本,致使用户遭受了此类脚本的攻击。
Dom Based型XSS
这种类型的XSS攻击我感觉可以是反射型也可以是存储型,所以肯定不是根据数据是不是存在数据库来划分的,只要是通过修改页面的DOM节点形成的XSS攻击,就是Dom Based型XSS攻击。这种攻击相对来说复杂一些,需要对html有一定的了解。看下面代码:
<!DOCTYPE html>
<html>
<head>
<title>Dom Based型XSS攻击</title>
<meta charset="utf-8"/>
</head>
<body>
<div>
<input type="text" id="input"></input>
<input type="button" id="btn" onclick="test()" value="生成链接">
<div id="link"></div>
</div>
<script type="text/javascript">
function test() {
var link = document.getElementById('input').value;
document.getElementById('link').innerHTML = '<a href="'+link+'">测试链接</a>';
}
</script>
</body>
</html>
我们本意是想生成一个可点击的链接,通过innerHTML将用户输入的链接写入页面中,这段代码就存在Dom Based XSS。我们在输入框输入:
" onclick=alert(/xss/)//
,输入之后点击生成的链接是这样: <a href="" onclick="alert(/xss/)//"">测试链接</a>
,第一个双引号是为了闭合href,然后插入一个onclick事件,//是为了注释第二个双引号。点击“测试链接”,脚本就会被执行:
还可以通过闭合掉<a>
标签,然后插入脚本的方式做到XSS攻击:
输入"><img src=1 onerror=alert(/xss/)//
,dom就会变为:<a href=""><img src="1" onerror="alert(/xss/)//"">测试链接</a>
。
这种攻击类型我们网站就曾经发生过,用户发现自己会自动给别人送礼,就是攻击者发现我们socket服务器没有对用户昵称进行校验和转义,攻击者通过闭合我们的nickName属性,构造送礼脚本就做到Dom Based XSS攻击。
XSS攻击成功后,攻击者能够对用户当前浏览的页面植入恶意脚本,攻击者可以做到任何javascript可以做到的任何事情。攻击一般会做哪些事情呢?
恶搞用户
攻击者或许只是输入骚扰类型的脚本,比如alert,弹窗等等,并不会对用户数据造成严重损失。
Cookie窃取
攻击者通过document.cookie获取到用户的cookie,发送到自己的服务器,攻击者就可以伪造用户的身份造谣撞骗啦。
构造Get与Post请求
除了输入验证码,输入旧密码等需要用户亲自发起的请求之外,攻击都可以在网站伪装成用户构造人意get和post请求来达到攻击者的目的。比如自动送礼,删除文章,修改用户数据等恶性行为等等。
XSS钓鱼
攻击者可以利用Javascript在当前页面弹出一个伪造的登录框,待用户输入用户名密码之后将用户的密码发送至攻击者的服务器上。
收集用户系统信息
攻击者可以收集用户的浏览器信息,然后借助一些浏览器插件,比如ActiveX,Java Applet等,从而获取到用户的系统等信息。
只要攻击者能力足够强大,想象力足够丰富,XSS能做的事情远超过我们的想象。
构造技巧,说到底就是利用网站漏洞想办法在用户浏览的网页中植入攻击脚本。掌握一些XSS常见的攻击技巧,对我们在开发过程中避免被攻击有重要意义,知道人家怎么攻,才知道自己该怎么防嘛。说说我所知道的一些技巧:
关闭标签
这是很常见的方式,通过关闭前一个标签达到植入XSS脚本的目的:"><script>alert("Hi");</script>
利用字符编码
通常,网站都会过滤或者转码<> ' " & %
之类的字符,这就没法直接通过插入标签的方式进行攻击,但是攻击者利用字符编码的方式往往可以绕过后台的过滤。比如<img src=x onerror=alert(1)>
,还是这段代码,我们通过编码试试:
JSunicode:
\u003c\u0069\u006d\u0067\u0020\u0073\u0072\u0063\u003d\u0031\u0020\u006f\u006e\u0065\u0072\u0072\u006f\u0072\u003d\u0061\u006c\u0065\u0072\u0074\u0028\u0031\u0029\u003e
JS8Hex:
\74\151\155\147\40\163\162\143\75\61\40\157\156\145\162\162\157\162\75\141\154\145\162\164\50\61\51\76
<input type="text" value="$var">
这段代码存在XSS,$var
长度限制是20,我们插入"><script>alert(/xss/)</script>
,就会被切为:"><script>alert(/xss
,就达不到攻击的目的。 " onclick=alert(1)//
,长度刚好20,呵呵!我们还可以通过eval(location.hash.substr(1))
等其它方式绕过服务端对输入长度的限制。解决XSS安全问题基本上就下面几条原则:
- 输入检查 用户输入的内容,过滤其中的特殊字符,如
",',<,>,&
;- 输出检查 输出用户输入的内容时,对其中不含的特殊字段进行转义,如
<
转为<
,>
转为>
;- 谨慎使用eval,innerHTML等js方法或属性;
- 设置HttpOnly防止JS读取Cookie
Html characters | Html Encode Entities |
---|---|
& | & |
< | < |
> | > |
" | " |
空格 | |
实现方式可以引入第三方库,或者类似下面这样处理方法:
function htmlEncode(c) {
switch(c) {
case '&':
return"&";
case '<':
return"<";
case '>':
return">";
case '"':
return""";
case ' ':
return" ";
default:
return c +"";
}
}
不要在页面中插入任何不可信数据,除非你已经确保这些数据已经进行了安全编码。下面列举一些常见的XSS漏洞场景,看看如何防御。
场景1:
通过 JavaScript 从 Cookie、URL、页面或数据库中获取数据,然后在页面上展示。
<div id="xss">
</div>
<script>
var a = location.hash.slice(1);
document.getElementById("xss").innerHTML = b;
</script>
从 Cookie、URL、页面或数据库中获取的数据,有可能包括恶意攻击的代码;在展示之前,需要使用 JavaScript 编写转义函数,将获取的数据中的 ",',<,>,& 进行HTML转义。
场景2:
直接在页面中输出不可信数据时,要确保对特殊字符有正确的转义。比如:
{{$var}}
<script>
var html = '<div>title is "{{$var}}".</div>';
document.write(html);
//或node.innerHTML = html;
</script>
{{$var}}
<script>
var html = '<a href="{{$var}}">test</a>';
document.write(html);
//或node.innerHTML = html;
</script>
{{$var}}
<script>
var html = '<input type="button" onclick="add(' + {{$var}} + ')">';
document.write(html);
//或node.innerHTML = html;
</script>
以上各类情况,都要确保对特殊字符有正确的转义。
- < 转成 `<`
- > 转成 `>`
- ' 转成 `'`
- " 转成 `"`
我们通过JS是无法读取得到的:
在开发过程中,不管是做前端还是后端的编码,安全意识很重要,需要时刻提醒自己,在输出任何来自不可信数据的时候有没有被注入的风险,只有才能让我们远离xss攻击。