XSS漏洞及防护
一、常见获取变量
request.getParameter()
request.getCookies()
如何防止xss漏洞
Asp server.htmlencode
Php escapehtmlspecialchars
防护转译函数:
<%!
String asHTML(String text)
{
if (text == null) return "";
StringBuffer results = null;
char[] orig = null;
int beg = 0, len = text.length();
for (int i = 0; i < len; ++i)
{
char c = text.charAt(i);
switch (c) {
case 0:
case '&':
case '<':
case '>':
case '"':
if (results == null)
{
orig = text.toCharArray();
results = new StringBuffer(len + 10);
}
if (i > beg) results.append(orig, beg, i - beg);
beg = i + 1;
switch (c) {
default: // case 0:
continue;
case '&':
results.append("&");
break;
case '<':
results.append("<");
break;
case '>':
results.append(">");
break;
case '"':
results.append("\"");
break;
}
break;
}
}
if (results == null) return text;
results.append(orig, beg, len - beg);
return results.toString();
}
%>
二、实战演练
测试路径http://127.0.0.1:8080/wavsep/index-xss.jsp
(1)、get反射型的XSS跨站
textarea文本里面Case2-Tag2TagScope.jsp
url get传参数直接在textvalue查看源码:
exp:
输入框的value里面Case3-Tag2TagStructure.jsp
exp:">
frame前端框架注入Case5-Tag2Frameset.jsp
exp:"><frame name=frame3 id=frame3 src="javascript:alert(‘thelostworld’);
查看出发修改的frame注入的脚本
src的连接触发Case9-SrcProperty2TagStructure.jsp
exp:value src=http://localhost:8080/wavsep/RXSS-Detection-Evaluation-GET/exploit.js
查看外部的资源链接
直接展示传入参数(Case1-Tag2HtmlPageScope.jsp)
POC提交:
<script>alert(/xss/)</script>
防护代码修改:
源代码:<%
if (request.getParameter("userinput") == null) {
%>
Enter your input:
<%
}
else {
try {
String userinput = request.getParameter("userinput");
out.println("The reflected value: " + userinput);
out.flush();
} catch (Exception e) {
out.println("Exception details: " + e);
}
} //end of if/else block
%>
-------------------------------------------------------------------------
添加函数模块asHTML参数过滤函数
<%!
String asHTML(String text)
{
if (text == null) return "";
StringBuffer results = null;
char[] orig = null;
int beg = 0, len = text.length();
for (int i = 0; i < len; ++i)
{
char c = text.charAt(i);
switch (c) {
case 0:
case '&':
case '<':
case '>':
case '"':
if (results == null)
{
orig = text.toCharArray();
results = new StringBuffer(len + 10);
}
if (i > beg) results.append(orig, beg, i - beg);
beg = i + 1;
switch (c) {
default: // case 0:
continue;
case '&':
results.append("&");
break;
case '<':
results.append("<");
break;
case '>':
results.append(">");
break;
case '"':
results.append("\"");
break;
}
break;
}
}
if (results == null) return text;
results.append(orig, beg, len - beg);
return results.toString();
}%>
<%
if (request.getParameter("userinput") == null) { //获取前端获取的参数
%>
Enter your input:
<%
}
else {
try {
String userinput = asHTML(request.getParameter("userinput")); //添加asHTML过滤函数
out.println("The reflected value: " + userinput);
out.flush();
} catch (Exception e) {
out.println("Exception details: " + e);
}
} //end of if/else block
%>
过滤函数注意事项:
1、需要使用一个完整的函数代码块在开始<%! …%> 需要添加一个!代表全局变量
2、注意函数的调用合适过滤的转换地方
修改过滤函数访问:全部将参数转译过滤
三、防护总结:
对于XSS跨站漏洞,可以采用以下修复方式:
1、验证所有输入数据,有效检测攻击;对所有输出数据进行适当的编码,以防止任何已成功注入的脚本在浏览器端运行。具体如下 :
输入验证:某个数据被接受为可被显示或存储之前,使用标准输入验证机制,验证所有输入数据的长度、类型、语法以及业务规则。
输出编码:数据输出前,确保用户提交的数据已被正确进行entity编码,建议对所有字符进行编码而不仅局限于某个子集。
明确指定输出的编码方式:不要允许攻击者为你的用户选择编码方式(如ISO 8859-1或 UTF 8)。
注意黑名单验证方式的局限性:仅仅查找或替换一些字符(如"<" ">"或类似"script"的关键字),很容易被XSS变种攻击绕过验证机制。
警惕规范化错误:验证输入之前,必须进行解码及规范化以符合应用程序当前的内部表示方法。请确定应用程序对同一输入不做两次解码。对客户端提交的数据进行过滤,一般建议过滤掉双引号(”)、尖括号(<、>)等特殊字符,或者对客户端提交的数据中包含的特殊字符进行实体转换,比如将双引号(”)转换成其实体形式",<对应的实体形式是<,<对应的实体形式是>以下为需过滤的常见字符:
[1] |(竖线符号)
[2] & (& 符号)
[3];(分号)
[4] $(美元符号)
[5] %(百分比符号)
[6] @(at 符号)
[7] '(单引号)
[8] "(引号)
[9] \'(反斜杠转义单引号)
[10] \"(反斜杠转义引号)
[11] <>(尖括号)
[12] ()(括号)
[13] +(加号)
[14] CR(回车符,ASCII 0x0d)
[15] LF(换行,ASCII 0x0a)
[16] ,(逗号)
[17] \(反斜杠)
2、在请求返回页面关键字符进行转义;[1] “(双引号):"
[2] ’ (单引号):&apos
[3] &(&符号):&
[4] <(左尖括号):<
[5] >(右尖括号):>
在不影响应用的前提下,建议将cookie标记为httpOnly,同时禁用TRACE方法。
2、对于java进行的web业务开发的过滤器如下:
在用java进行web业务开发的时候,对于页面上接收到的参数,除了极少数是不可预知的内容外,大量的参数名和参数值都是不会出现触发Xss漏洞的字符。而通常为了避免Xss漏洞,都是开发人员各自在页面输出和数据入库等地方加上各种各样的encode方法来避免Xss问题。而由于开发人员的水平不一,加上在编写代码的过程中安全意识的差异,可能会粗心漏掉对用户输入内容进行encode处理。针对这种大量参数是不可能出现引起Xss和SQL注入漏洞的业务场景下,因此可以使用一个适用大多数业务场景的通用处理方法,牺牲少量用户体验,来避免Xss漏洞和SQL注入。
利用Servlet的过滤器机制,编写定制的XssFilter,将request请求代理,覆盖getParameter和getHeader方法将参数名和参数值里的指定半角字符,强制替换成全角字符。使得在业务层的处理时不用担心会有异常输入内容。
XssFilter.java
package filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
public class XssFilter implements Filter {
public void init(FilterConfig config) throws ServletException {
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException
{
XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper(
(HttpServletRequest) request);
chain.doFilter(xssRequest, response);
}
public void destroy() {
}
XssHttpServletRequestWrapper.java
package filter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
HttpServletRequest orgRequest = null;
public XssHttpServletRequestWrapper(HttpServletRequest request) {
super(request);
orgRequest = request;
}
/**
* 覆盖getParameter方法,将参数名和参数值都做xss过滤。
* 如果需要获得原始的值,则通过super.getParameterValues(name)来获取
* getParameterNames,getParameterValues和getParameterMap也可能需要覆盖
*/
@Override
public String getParameter(String name) {
String value = super.getParameter(xssEncode(name));
if (value != null) {
value = xssEncode(value);
}
return value;
}
/**
* 覆盖getHeader方法,将参数名和参数值都做xss过滤。
* 如果需要获得原始的值,则通过super.getHeaders(name)来获取
* getHeaderNames 也可能需要覆盖
*/
@Override
public String getHeader(String name) {
String value = super.getHeader(xssEncode(name));
if (value != null) {
value = xssEncode(value);
}
return value;
}
/**
* 将容易引起xss漏洞的半角字符直接替换成全角字符
*
* @param s
* @return
*/
private static String xssEncode(String s) {
if (s == null || s.isEmpty()) {
return s;
}
StringBuilder sb = new StringBuilder(s.length() + 16);
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
switch (c) {
case '>':
sb.append('>');//全角大于号
break;
case '<':
sb.append('<');//全角小于号
break;
case '\'':
sb.append('‘');//全角单引号
break;
case '\"':
sb.append('“');//全角双引号
break;
case '&':
sb.append('&');//全角
break;
case '\\':
sb.append('\');//全角斜线
break;
case '#':
sb.append('#');//全角井号
break;
default:
sb.append(c);
break;
}
}
return sb.toString();
}
/**
* 获取最原始的request
*
* @return
*/
public HttpServletRequest getOrgRequest() {
return orgRequest;
}
/**
* 获取最原始的request的静态方法
*
* @return
*/
public static HttpServletRequest getOrgRequest(HttpServletRequest req) {
if(req instanceof XssHttpServletRequestWrapper){
return ((XssHttpServletRequestWrapper)req).getOrgRequest();
}
return req;
}
在web.xml中添加
<filter>
<filter-name>xssFilter</filter-name>
<filter-class>filter.XssFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>xssFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
对于PHP语言所编写的网站系统的XSS过滤器:
<?php
/**
* @去除XSS(跨站脚本攻击)的函数
* @par $val 字符串参数,可能包含恶意的脚本代码如<script language="javascript">alert("hello world");</script>
* @return 处理后的字符串
* @Recoded By Androidyue
**/
function RemoveXSS($val) {
// remove all non-printable characters. CR(0a) and LF(0b) and TAB(9) are allowed
// this prevents some character re-spacing such as// note that you have to handle splits with \n, \r, and \t later since they *are* allowed in some inputs
$val = preg_replace('/([\x00-\x08,\x0b-\x0c,\x0e-\x19])/', '', $val);
// straight replacements, the user should never need these since they're normal characters
// this prevents like
$search = 'abcdefghijklmnopqrstuvwxyz';
$search .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$search .= '1234567890!@#$%^&*()';
$search .= '~`";:?+/={}[]-_|\'\\';
for ($i = 0; $i < strlen($search); $i++) {
// ;? matches the ;, which is optional
// 0{0,7} matches any padded zeros, which are optional and go up to 8 chars
// @ @ search for the hex values
$val = preg_replace('/(&#[xX]0{0,8}'.dechex(ord($search[$i])).';?)/i', $search[$i], $val); // with a ;
// @ @ 0{0,7} matches '0' zero to seven times
$val = preg_replace('/(�{0,8}'.ord($search[$i]).';?)/', $search[$i], $val); // with a ;
}
// now the only remaining whitespace attacks are \t, \n, and \r
$ra1 = Array('javascript', 'vbscript', 'expression', 'applet', 'meta', 'xml', 'blink', 'link', 'style', 'script', 'embed', 'object', 'iframe', 'frame', 'frameset', 'ilayer', 'layer', 'bgsound', 'title', 'base');
$ra2 = Array('onabort', 'onactivate', 'onafterprint', 'onafterupdate', 'onbeforeactivate', 'onbeforecopy', 'onbeforecut', 'onbeforedeactivate', 'onbeforeeditfocus', 'onbeforepaste', 'onbeforeprint', 'onbeforeunload', 'onbeforeupdate', 'onblur', 'onbounce', 'oncellchange', 'onchange', 'onclick', 'oncontextmenu', 'oncontrolselect', 'oncopy', 'oncut', 'ondataavailable', 'ondatasetchanged', 'ondatasetcomplete', 'ondblclick', 'ondeactivate', 'ondrag', 'ondragend', 'ondragenter', 'ondragleave', 'ondragover', 'ondragstart', 'ondrop', 'onerror', 'onerrorupdate', 'onfilterchange', 'onfinish', 'onfocus', 'onfocusin', 'onfocusout', 'onhelp', 'onkeydown', 'onkeypress', 'onkeyup', 'onlayoutcomplete', 'onload', 'onlosecapture', 'onmousedown', 'onmouseenter', 'onmouseleave', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onmousewheel', 'onmove', 'onmoveend', 'onmovestart', 'onpaste', 'onpropertychange', 'onreadystatechange', 'onreset', 'onresize', 'onresizeend', 'onresizestart', 'onrowenter', 'onrowexit', 'onrowsdelete', 'onrowsinserted', 'onscroll', 'onselect', 'onselectionchange', 'onselectstart', 'onstart', 'onstop', 'onsubmit', 'onunload');
$ra = array_merge($ra1, $ra2);
$found = true; // keep replacing as long as the previous round replaced something
while ($found == true) {
$val_before = $val;
for ($i = 0; $i < sizeof($ra); $i++) {
$pattern = '/';
for ($j = 0; $j < strlen($ra[$i]); $j++) {
if ($j > 0) {
$pattern .= '(';
$pattern .= '(&#[xX]0{0,8}([9ab]);)';
$pattern .= '|';
$pattern .= '|(�{0,8}([9|10|13]);)';
$pattern .= ')*';
}
$pattern .= $ra[$i][$j];
}
$pattern .= '/i';
$replacement = substr($ra[$i], 0, 2).''.substr($ra[$i], 2); // add in <> to nerf the tag
$val = preg_replace($pattern, $replacement, $val); // filter out the hex tags
if ($val_before == $val) {
// no replacements were made, so exit the loop
$found = false;
}
}
}
return $val;
}
//测试一下效果
//echo RemoveXSS(“”) ;
?>
asp程序网站出现SQL注入 跨站脚本攻击(XSS)漏洞的时候可以使用
源码如下:url=trim(request(“url”))
修改如下:url=Server.HTMLEncode(trim(request(“url”)))
使用Server.HTMLEncode它来解决脚本跨站攻击漏洞
或者:
对用户输入区域只允许适应应用功能所必需的规定字符输入,,对于合法的“>”、“<”“&”这三个符号进行替换:
replace(str,"<","<")
replace(str,">",">")
replace(str,"&","&")
3、防护转译函数:
<%!
String asHTML(String text)
{
if (text == null) return "";
StringBuffer results = null;
char[] orig = null;
int beg = 0, len = text.length();
for (int i = 0; i < len; ++i)
{
char c = text.charAt(i);
switch (c) {
case 0:
case '&':
case '<':
case '>':
case '"':
if (results == null)
{
orig = text.toCharArray();
results = new StringBuffer(len + 10);
}
if (i > beg) results.append(orig, beg, i - beg);
beg = i + 1;
switch (c) {
default: // case 0:
continue;
case '&':
results.append("&");
break;
case '<':
results.append("<");
break;
case '>':
results.append(">");
break;
case '"':
results.append("\"");
break;
}
break;
}
}
if (results == null) return text;
results.append(orig, beg, len - beg);
return results.toString();
}
%>
四、课后了解
http://baike.baidu.com/view/50325.htm
常规的XSS的攻击方式
公众号:
二维码:
个人知乎:https://www.zhihu.com/people/fu-wei-43-69/columns
个人简书:https://www.jianshu.com/u/bf0e38a8d400