『跨站脚本攻击』CSRF攻击
本文主要介绍如何对XSS攻击进行防御
HttpOnly
定义:浏览器禁止页面的JS访问带有HttpOnly属性的Cookie。
HttpOnly并非防止出现XSS,而是防御XSS后的Cookie劫持攻击。
作用方式
在服务器对浏览器的请求进行回复,Set-Cookie时对cookie进行标记,服务器可能会设置多个Cookie,而HttpOnly可以有选择性地加在任何一个Cookie值上,因此可以仅将其加在用于认证的关键cookie上面。
输入检查
定义:
目前Web开发的普遍做法,是同时在客户端JavaScript中和服务器端代码中实现相同的输入检查。
原因:
- 客户端检查提高用户体验:在客户端进行输入验证可以即时反馈错误信息给用户,无需等待服务器响应。节约了服务器资源的同时,也可以快速指导用户纠正错误,提高整体的用户体验。
- 服务端检查保障安全性:尽管客户端验证可以提供即时反馈,但它可以被绕过。用户可以修改客户端代码,或者直接通过HTTP请求工具发送请求绕开客户端。因此,服务端验证是必不可少的,它保证了即使客户端验证被绕过,服务端仍然能够保护应用免受恶意数据的影响。
作用方式:存在比较多的开源xssfilter
局限性
1)语境
XSS Filter在用户提交数据时获取变量,并进行XSS检查;但此时用户数据并没有结合渲染页面的HTML代码,因此XSS Filter对语境的理解并不完整。
例如:
1
<script src="$var" ></script>
其中
$var
是用户可以控制的变量。用户只需要提交一个恶意脚本所在的URL地址,即可实施XSS攻击。如果是一个全局性的XSS Filter,则无法看到用户数据的输出语境,而只能看到用户提交了一个URL,很可能会漏报。因为在大多数情况下,URL是一种合法的用户数据。
2)字符处理
如果仅仅是对
<
、"
等字符进行粗暴的转义或替换处理,可能会改变用户数据的语义。
输出检查
安全的编码函数
可以使用编码函数对输出中的敏感符号进行转义
例如:JavascriptEncode对除数字、字母以外的所有字符,使用十六进制的方式进行编码。
还有很多的编码函数,如XMLEncode、JSONEncode等。
在转义的时候要关注上下文环境
不同的上下文环境(HTML文档结构和JavaScript程序逻辑)对数据的解析方式不同。如果在不适当的上下文中使用错误的转义方法,可能不会产生预期的防护效果,仍然允许XSS攻击的发生。所以要在正确的地方使用正确的编码方式。
例如:
a. 初始HTML代码
1
2
3htmlCopy code<body>
<a href=# onclick="alert('$var');">test</a>
</body>这里的意图是在用户点击链接时弹出变量
$var
的内容。b. 用户输入和攻击向量
1
2
3javascript
Copy code
$var = htmlencode("');alert('2");用户通过输入特制的字符串试图结束原有的
alert
函数调用,并开始一个新的alert
函数,实际上注入了额外的JavaScript代码。c. HTML编码应用后的结果
1
2
3htmlCopy code<body>
<a href=# onclick="alert('');alert('2');">test</a>
</body>这里,虽然特殊字符被HTML编码,看似安全,但问题在于浏览器的解析顺序。
浏览器解析流程:
- HTML解析器(HTMLParser)优先运行,它会解码HTML实体,从而恢复JavaScript代码中的特殊字符。
- JavaScript解析器(JavaScript Parser)随后执行,此时JavaScript代码已经被“修复”成可以执行的形式。
因此,最终执行的JavaScript为:
1
2
3javascript
Copy code
alert('');alert('2');这实现了攻击者的目的:在页面中注入了额外的JavaScript代码。
各种场景下的XSS防御
想要根治XSS问题,可以列出所有XSS可能发生的场景,再一一解决。
下面用变量“$var”表示用户数据,它将被填充入HTML代码中。
可能存在以下场景:
在HTML标签中输出
例子
1
2<div>$var</div>
<a href=# >$var</a>利用方式
一般是构造一个
<script>
标签,或者是任何能够产生脚本执行的方式。比如1
<div><script>alert(/xss/)</script></div>
或者
1
<a href=# ><img src=# onerror=alert(1) /></a>
防御方法
对变量使用HtmlEncode。
在HTML属性中输出
例子
1
<div id="abc" name="$var" ></div>
利用方式
使用
"
闭合属性引号1
<div id="abc" name=""><script>alert(/xss/)</script><"" ></div>
防御方法
HtmlEncode。
在script标签中输出
例子
1
2
3<script>
var x = "$var";
</script>利用方式
使用
"
闭合引号1
2
3<script>
var x = "";alert(/xss/);//";
</script>防御方法
JavascriptEncode
在事件中输出
例子
1
<a href=# onclick="funcA('$var')" >test</a>
利用方式
闭合引号
1
<a href=# onclick="funcA('');alert(/xss/);//')" >test</a>
防御方法
JavascriptEncode
在CSS中输出
例子与利用方式
1
2
3
4
5
6
7
8
9
10
11
12# 通过@import指令从恶意URL导入一个CSS文件
<STYLE>@import'http://ha.ckers.org/xss.css';</STYLE>
# 从指定的URL加载绑定文件,该文件可以包含恶意的XBL
<STYLE>BODY{-moz-binding:url("http://ha.ckers.org/xssmoz.xml#xss")}</STYLE>
# behavior属性用于将特定的行为(通常是HTC文件,一种微软专有的HTML组件)附加到页面元素。
<XSS STYLE="behavior: url(xss.htc);">
# 利用CSS中的list-style-image属性,插入JavaScript代码,该代码在渲染列表项图像时执行。
<STYLE>li {list-style-image: url("javascript:alert('XSS')");}</STYLE><UL><LI>XSS
# 通过background-image属性插入JavaScript代码。
<DIV STYLE="background-image: url(javascript:alert('XSS'))">
# expression是一个允许CSS属性值为JavaScript表达式的特性。这可以用来执行JavaScript代码。
<DIV STYLE="width: expression(alert('XSS'));">防御方法
尽可能地禁止用户控制的变量在
<style>
标签、HTML标签的style属性以及CSS文件中输出。可以使用encodeForCSS()函数
在地址中输出
例子
1
<a href="http://www.evil.com/?test=$var" >test</a>
利用方式
一个url的组成一般如下:
1
[Protocal][Host][Path][Search][Hash]
如:
1
2
3
4
5
6https://www.a.com/a/b/c/test?abc=123#sss
[Protocal]="https://"
[Host]="www.a.com"
[Path]="/a/b/c/test"
[Search]="?abc=123"
[Hash]="#ssss"攻击者可能会构造伪协议实施攻击,包括javascript、vbscript、dataURI等
1
<a href="javascript:alert(1);" >test</a>
闭合引号
1
<a href=# onclick="funcA('');alert(/xss/);//')" >test</a>
防御方法
检查URL的协议:确保URL以“http”或“https”开头,如果不是,则自动添加。
URLEncode路径和参数:在确保协议和主机部分正确后,再对路径和参数部分进行URLEncode。
在用户能够完全控制URL的情况下,Protocal和Host部分不能使用URLEncode,否则会改变URL的语义。
富文本处理
什么是富文本:
1 | 有些时候,网站需要允许用户提交一些自定义的HTML代码,称之为“富文本”。 |
如何处理富文本:
- 通过htmlparser可以解析出HTML代码的标签、标签属性和事件。
- 禁止事件
- 标签的选择:使用白名单,只允许
<a>
、<img>
、<div>
等比较“安全”的标签存在。 - 自定义CSS:尽量禁止
DOM Based XSS 防御
前文提到的方法都是针对 “从服务器应用直接输出到HTML页面” 的XSS漏洞,并不适用于DOM Based XSS。
DOM Based XSS漏洞发生在客户端,浏览器在解析和执行JavaScript代码时引入了恶意脚本。
例子1
1
2
3
4
5
6
7
8
9
10
11<script>
function test(){
var str = document.getElementById("text").value;
//将 div 元素 (id="t") 的内部 HTML 设置为一个带有 str 作为链接地址的 <a> 元素。链接的文本显示为 testLink。
document.getElementById("t").innerHTML = "<a href='"+str+"' >testLink</a>";
}
</script>
<div id="t" ></div>
<input type="text" id="text" value="" />
<input type="button" id="s" value="write" onclick="test()" />在button的onclick事件中,执行了test()函数,而该函数将text标签的输入值写入了DOM节点,最后导致了XSS的发生。
例子2
1
2
3
4
5//code1
<script>
var x="$var";
document.write("<a href='"+x+"'>test</a>");
</script>变量
$var
被写入html页面中,可能产生xss攻击。假设用户输入
$var
的值为onclick=alert(1);//
,这个值被发送给了服务器端,服务器对其进行了javascriptEscape
转义处理1
2
3
4
5//code2
<script>
var x="\x20\x27onclick\x3dalert\x281\x29\x3b\x2f\x2f\x27";
document.write{"<a href='"+x+"'>test</a>"};
</script>转义后的值被嵌入到HTML模板中,并传递到客户端浏览器。
但是,这个转义是在服务器端完成的,目的是防止直接在服务器端生成的HTML中执行恶意代码。然而,转义后的字符串被传递到客户端后,浏览器会重新解析这些转义字符,使其恢复原始的恶意代码。
当浏览器解析并执行这段JavaScript代码的时候,浏览器重新渲染页面,转义后的结果在
document.write
执行时,浏览器会将转义字符还原为:1
<a href='' onclick=alert(1);//''>test</a>
点击
test
链接会触发onclick
事件,成功注入XSS。为什么会出现这样的转义无效现象??
首先,客户端对x的转义在javascript中生效了
1
var x="\x20\x27onclick\x3dalert\x281\x29\x3b\x2f\x2f\x27"; (1)
当这段代码执行的时候,JavaScript解释器将这些转义字符解析为对应的实际字符
1
var x = " 'onclick=alert(1);//' "; (2)
注意,这里因为转义的存在,这段javascript代码中将x的内容
'onclick=alert(1);//'
视作了字符串,并不会对其进行执行但是后续document.write执行的时候,会将
x
的内容(已经完成解析,即为上面(2)中的内容)插入到HTML中,而这段在html上插入时没有进行再转义,产生了xss漏洞。防御方法
JS输出到HTML页面,也相当于一次XSS输出过程。需要分语境使用不同的编码函数。
首先,当”$var”输出到
<script>
时,应该执行一次javascriptEncode这一步是在将用户输入赋值给 JavaScript 变量时进行的编码,确保输入不会被解释为 JavaScript 代码。
在服务器端和客户端都可以进行编码
其次,document.write输出到HTML,要分2种情况
- 如果document.write输出到事件或者脚本,再做一次javascriptEncode
- 如果document.write输出到HTML内容或者属性,则做一次HtmlEncode
DOM based XSS的触发点
JS输出到HTML页面
需要重点关注这几个地方的参数是否可以被用户控制
1
2
3
4
5
6
7
8
9document.write()
document.writeln()
xxx.innerHTML=
xxx.outerHTML=
innerHTML.replace
document.attachEvent()
window.attachEvent()
document.location.replace()
document.location.assign()其他
1
2
3
4
5
6
7页面中所有的inputs框
window.location(href、hash等)
window.name
document.referrer
document.cookie
localstorage
XMLHttpRequest返回的数据