xss 攻击过程
跨站脚本攻击(Cross Site Scripting), 恶意攻击者往Web页面里插入恶意Script代码,当用户浏览该页之时,嵌入其中Web里面的Script代码会被执行,从而达到恶意攻击用户的特殊目的。
xss 的危害
XSS攻击的危害
这些危害包括但不局限于∶盗取管理员或普通用户cookie、session ;
读取、篡改、添加、删除敏感数据;
网站挂马;
非法转账;
控制受害者机器向其它网站发起攻击
xss 的三种类型
反射型XSS:
只是简单地把用户输入的数据反射给浏览器,黑客需要诱使用户点击链接。当目标用户访问该链接时,服务器接收该用户目标的请求并进行处理,然后服务器把带有XSS代码的数据发送给目标用户的浏览器,浏览器解析这段带有XSS代码的恶意脚本后,就会触发XSS漏洞。反射型XSS又称为非持久性XSS,这种攻击方式具有一次性。
存储型XSS:
把用户输入的数据”存储“在服务器端。这种XSS具有很强的稳定性。比较常见的一个场景是,攻击者写下一篇包含恶意Javascript代码的博客文章,文章发表后,所有访问
该博客文章的用户,都会在他们的浏览器中执行这段恶意的Javascript代码。黑客把恶意的脚本保存在服务器端。存储型XSS也叫持久性XSS。
DOM based XSS:
DOM全称Document Object Model,使用DOM可以使程序和脚本能够动态访问和更新文档的内容、结构以及样式;
DOM型XSS从效果上来说也是一种反射型XSS,是基于DOM文档对象模型的一种漏洞;
HTML的标签都是节点,这些节点组成了DOM的整体结构—节点树。通过HTML DOM,树中的所有节点均可通过JavaScript进行访问。所有HTML元素(节点)均可被修改,也可以创建或删除节点。
实现原因
存入–数据库–编译–执行
必须具备以上几个条件xss攻击才会产生,即服务器原样编译了用户输入的js或者HTML
假如有下面一个textbox
<input type="text" name="address1" value="value1from">
value1from是来自用户的输入,如果用户不是输入value1from,而是输入
"/><script>alert(document.cookie)</script><!- 那么就会变成
<input type="text" name="address1" value=""/><script>alert(document.cookie)</script><!- ">
嵌入的JavaScript代码将会被执行或者用户输入的是
“οnfοcus=”alert(document.cookie) 那么就会变成
<input type="text" name="address1" value="" onfocus="alert(document.cookie)">
事件被触发的时候嵌入的JavaScript代码将会被执行攻击的威力,取决于用户输入了什么样的脚本,当然用户提交的数据还可以通QueryString(放在URL中)和Cookie发送给服务器。
xss 的防御
XSS之所以会发生, 是因为用户输入的数据变成了代码。 所以我们需要对用户输入的数据进行HTML Encode处理。 将其中的”中括号”, “单引号”,“引号” 之类的特殊字符进行编码。
xss的三种防范方式
1、编码:
对用户输入的数据进行HTML Entity 编码。把字符转换成 转义字符。Encode的作用是将$var等一些字符进行转化,使得浏览器在最终输出结果上是一样的。
比如说这段代码:’<‘script>alert(1)’<’/script>
若不进行任何处理,则浏览器会执行alert的js操作,实现XSS注入。进行编码处理之后,L在浏览器中的显示结果就是这个文本,将变量作为纯文本进行输出,且不引起JavaScript的执行。
2、过滤:
移除用户输入的和事件相关的属性。如onerror可以自动触发攻击,还有onclick等。(总而言是,过滤掉一些不安全的内容)移除用户输入的Style节点、Script节点、Iframe节点。(尤其是Script节点,它可是支持跨域的呀,一定要移除)。
3、校正:
避免直接对HTML Entity进行解码。使用DOM Parse转换,校正不配对的DOM标签。(DOM Parse:它的作用是把文本解析成DOM结构)
常见2种方法:a.第一步的编码转成文本,然后第三步转成DOM对象,然后经过第二步的过滤。 b.还有一种更简洁:首先是encode,如果是富文本,就白名单
Java代码解决方案
1、自己写 filter 拦截来实现,但要注意的时,在WEB.XML 中配置 filter 的时候,将这个 filter 放在第一位。
package com.mtlk.wd;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class CharFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
MyRequestWrapper rw = new MyRequestWrapper(request);
filterChain.doFilter(rw,response);
}
@Override
public void destroy() {
}
}
2、继承HttpServletRequestWrapper,实现对请求参数的过滤
/**
* xss请求适配器
*/
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
public XssHttpServletRequestWrapper(HttpServletRequest request) {
super(request);
}
/**
* 对数组参数进行特殊字符过滤
*/
@Override
public String[] getParameterValues(String name) {
String[] values = super.getParameterValues(name);
if (values == null) {
return null;
}
int count = values.length;
String[] encodedValues = new String[count];
for (int i = 0; i < count; i++) {
encodedValues[i] = cleanXSS(values[i]);
}
return encodedValues;
}
/**
* 对参数中特殊字符进行过滤
*/
@Override
public String getParameter(String name) {
String value = super.getParameter(name);
if (value == null) {
return null;
}
return cleanXSS(value);
}
/**
* 获取attribute,特殊字符过滤
*/
@Override
public Object getAttribute(String name) {
Object value = super.getAttribute(name);
if (value != null && value instanceof String) {
cleanXSS((String) value);
}
return value;
}
/**
* 对请求头部进行特殊字符过滤
*/
@Override
public String getHeader(String name) {
String value = super.getHeader(name);
if (value == null) {
return null;
}
return cleanXSS(value);
}
/**
* 转义字符,使用该方法存在一定的弊端
*
* @param value
* @return
*/
private String cleanXSS2(String value) {
// 移除特殊标签
value = value.replaceAll("<", "<").replaceAll(">", ">");
value = value.replaceAll("\\(", "(").replaceAll("\\)", ")");
value = value.replaceAll("'", "'");
value = value.replaceAll("eval\\((.*)\\)", "");
value = value.replaceAll("[\\\"\\\'][\\s]*javascript:(.*)[\\\"\\\']", "\"\"");
value = value.replaceAll("script", "");
return value;
}
private String cleanXSS(String value) {
if (value != null) {
//推荐使用ESAPI库来避免脚本攻击,value = ESAPI.encoder().canonicalize(value);
// 避免空字符串
value = value.replaceAll(" ", "");
// 避免script 标签
Pattern scriptPattern = Pattern.compile("<script>(.*?)</script>", Pattern.CASE_INSENSITIVE);
value = scriptPattern.matcher(value).replaceAll("");
// 避免src形式的表达式
scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\'(.*?)\\\'",
Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
value = scriptPattern.matcher(value).replaceAll("");
scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\"(.*?)\\\"",
Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
value = scriptPattern.matcher(value).replaceAll("");
// 删除单个的 </script> 标签
scriptPattern = Pattern.compile("</script>", Pattern.CASE_INSENSITIVE);
value = scriptPattern.matcher(value).replaceAll("");
// 删除单个的<script ...> 标签
scriptPattern = Pattern.compile("<script(.*?)>",
Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
value = scriptPattern.matcher(value).replaceAll("");
// 避免 eval(...) 形式表达式
scriptPattern = Pattern.compile("eval\\((.*?)\\)",
Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
value = scriptPattern.matcher(value).replaceAll("");
// 避免 expression(...) 表达式
scriptPattern = Pattern.compile("expression\\((.*?)\\)",
Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
value = scriptPattern.matcher(value).replaceAll("");
// 避免 javascript: 表达式
scriptPattern = Pattern.compile("javascript:", Pattern.CASE_INSENSITIVE);
value = scriptPattern.matcher(value).replaceAll("");
// 避免 vbscript:表达式
scriptPattern = Pattern.compile("vbscript:", Pattern.CASE_INSENSITIVE);
value = scriptPattern.matcher(value).replaceAll("");
// 避免 οnlοad= 表达式
scriptPattern = Pattern.compile("onload(.*?)=",
Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
value = scriptPattern.matcher(value).replaceAll("");
}
return value;
}
}