XSS攻击类似于SQL注入攻击,攻击之前,我们先找到一个存在XSS漏洞的网站,XSS漏洞分为两种,一种是DOM Based XSS漏洞,另一种是Stored XSS漏洞。理论上,所有可输入的地方没有对输入数据进行处理的话,都会存在XSS漏洞,漏洞的危害取决于攻击代码的威力,攻击代码也不局限于script。
类似的攻击还有XSRF,XSRF是在本地生成cookie,模仿或者伪造跨域请求者信息,使用工具或者伪代码向后端发起请求,达到攻击的目的
XSS攻击手方式主要有反射型和存储型
1.反射型:发出请求时候,XSS代码出现在URL中,作为请求提交到服务器中,服务器解析后响应,这样的一个过程叫做反射型XSS
例子:
假设有一个请求是:
http://ip:port/index
XSS攻击是请求地址为:
http://ip:port/index?xss=
不单单是javascript,普通的DOM也可以
http://ip:port/index?xss=
http://ip:port/index?xss=
2.存储型:存储型将提交的代码存储在服务器端(数据库、内存、文件系统等),下次请求目标页面时不用再提交XSS代码
通过编码的统一,HTML Entity编码,就是将代码进行转义,比如>转义为>
等
过滤掉等敏感,移除用户上传的Dom属性,如onerror等,移除用户上传的
style
节点、script
节点、iframe
、frame
节点等
建议用户所有的事件全部要过滤掉,onclick、onerror等
如果攻击用户设置了style,假如被用户设置body为display:none,那么正常用户看到整个body都会变成空白,达到攻击的效果
避免直接对Html Entity解码,使用Dom parse转换,校正不配对的Dom标签
自定义HttpServletRequestWrapper
import java.util.Enumeration;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
public XssHttpServletRequestWrapper(HttpServletRequest servletRequest) {
super(servletRequest);
}
public String[] getParameterValues(String parameter) {
String[] values = super.getParameterValues(parameter);
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;
}
public String getParameter(String parameter) {
String value = super.getParameter(parameter);
if (value == null) {
return null;
}
return cleanXSS(value);
}
public String getHeader(String name) {
String value = super.getHeader(name);
if (value == null)
return null;
return cleanXSS(value);
}
private String cleanXSS(String value) {
//You'll need to remove the spaces from the html entities below
value = value.replaceAll("<", "& lt;").replaceAll(">", "& gt;");
value = value.replaceAll("\\(", "& #40;").replaceAll("\\)", "& #41;");
value = value.replaceAll("'", "& #39;");
value = value.replaceAll("eval\\((.*)\\)", "");
value = value.replaceAll("[\\\"\\\'][\\s]*javascript:(.*)[\\\"\\\']", "\"\"");
value = value.replaceAll("script", "");
return value;
}
}
新建拦截器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;
import javax.servlet.http.HttpServletResponse;
public class XssFilter implements Filter {
FilterConfig filterConfig = null;
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
}
public void destroy() {
this.filterConfig = null;
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
chain.doFilter(new XssHttpServletRequestWrapper(
(HttpServletRequest) request), response);
}
}
配置拦截器
<filter>
<filter-name>XssSqlFilterfilter-name>
<filter-class>com.ibm.web.beans.XssFilterfilter-class>
filter>
<filter-mapping>
<filter-name>XssSqlFilterfilter-name>
<url-pattern>/*url-pattern>
<dispatcher>REQUESTdispatcher>
filter-mapping>
CSRF攻击形式其实很简单,正因为它的简单反而容易被人忽视,因此它的危害非常巨大。其攻击形式为:用户(USER)首先登录一正常网站(Normal Website),正常网站向用户浏览器发送cookie信息,此时用户还未注销登录信息。然后用户又打开浏览器另一标签页(tab)访问了一个恶意网站(Malicious Website),Malicious Website会自动构造指向Normal Website的恶意HTTP请求(更新、删除、添加您的重要信息)
1)假如系统没有权限控制,通过跨域伪造来达到攻击的效果
加入非正常用户通过分析网页得到网站的删除链接是del_user_posts
,那么在别的系统通过修改id来删除系统中的数据,这个是非常可怕的
2)攻击实例
比如说,受害者 Bob 在银行有一笔存款,通过对银行的网站发送请求 http://bank.example/withdraw?account=bob&amount=1000000&for=bob2
可以使 Bob 把 1000000 的存款转到 bob2 的账号下。
通常情况下,该请求发送到网站后,服务器会先验证该请求是否来自一个合法的 session,并且该 session 的用户 Bob 已经成功登陆。黑客 Mallory 自己在该银行也有账户,他知道上文中的 URL 可以把钱进行转帐操作。Mallory 可以自己发送一个请求给银行:http://bank.example/withdraw?account=bob&amount=1000000&for=Mallory
。但是这个请求来自 Mallory 而非 Bob,他不能通过安全认证,因此该请求不会起作用。这时,Mallory 想到使用 CSRF 的攻击方式,他先自己做一个网站,在网站中放入如下代码: src="http://bank.example/withdraw?account=bob&amount=1000000&for=Mallory "
,并且通过广告等诱使 Bob 来访问他的网站。当 Bob 访问该网站时,上述 url 就会从 Bob 的浏览器发向银行,而这个请求会附带 Bob 浏览器中的 cookie 一起发向银行服务器。大多数情况下,该请求会失败,因为他要求 Bob 的认证信息。但是,如果 Bob 当时恰巧刚访问他的银行后不久,他的浏览器与银行网站之间的 session 尚未过期,浏览器的 cookie 之中含有 Bob 的认证信息。这时,悲剧发生了,这个 url 请求就会得到响应,钱将从 Bob 的账号转移到 Mallory 的账号,而 Bob 当时毫不知情。等以后 Bob 发现账户钱少了,即使他去银行查询日志,他也只能发现确实有一个来自于他本人的合法请求转移了资金,没有任何被攻击的痕迹。而 Mallory 则可以拿到钱后逍遥法外。
Referer带有网站的域名信息,后台可以校验这个域名是否是本站点,如果不是,直接拒绝
// 从 HTTP 头中取得 Referer 值
String referer=request.getHeader("Referer");
// 判断 Referer 是否以 bank.example 开头
if((referer!=null) &&(referer.trim().startsWith(“bank.example”))){
chain.doFilter(request, response);
}else{
request.getRequestDispatcher(“error.jsp”).forward(request,response);
}
用户请求存在与Cookie中,假设服务器每次都会产生一个随机Token,返回到浏览器中,浏览器中下次请求必须带上此Token,服务器有一个Filter来校验此Token,如果Token不正确,则拒绝此请求
HttpServletRequest req = (HttpServletRequest)request;
HttpSession s = req.getSession();
// 从 session 中得到 csrftoken 属性
String sToken = (String)s.getAttribute(“csrftoken”);
if(sToken == null){
// 产生新的 token 放入 session 中
sToken = generateToken();
s.setAttribute(“csrftoken”,sToken);
chain.doFilter(request, response);
} else{
// 从 HTTP 头中取得 csrftoken
String xhrToken = req.getHeader(“csrftoken”);
// 从请求参数中取得 csrftoken
String pToken = req.getParameter(“csrftoken”);
if(sToken != null && xhrToken != null && sToken.equals(xhrToken)){
chain.doFilter(request, response);
}else if(sToken != null && pToken != null && sToken.equals(pToken)){
chain.doFilter(request, response);
}else{
request.getRequestDispatcher(“error.jsp”).forward(request,response);
}
}
通过 XMLHttpRequest 这个类,可以一次性给所有该类请求加上 csrftoken 这个 HTTP 头属性,并把 token 值放入其中。局限性较大,只适合Ajax
使用HandlerInterceptorAdapter
public class CSRFTokenInterceptor extends HandlerInterceptorAdapter {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws IOException {
String token = CookieUtils.getCookie("csrf_token"); //从cookie中读取token
if (request.getMethod().equalsIgnoreCase("POST")) {
String requestType = request.getHeader("X-Requested-With");
//AJAX POST 请求
if (requestType != null && requestType.equalsIgnoreCase("XMLHttpRequest")) {
if (token != null && token.equals(request.getHeader("csrf_token")))
return true;
} else if (token != null && token.equals(request.getParameter("csrf_token"))) {//普通表单POST方法
return true;
}
if (token == null) {
token = UUID.randomUUID().toString();
CookieUtils.addCookie(request, response, "csrf_token", token);
}
//SC_FORBIDDEN,状态码是403,表示服务器明白客户的请求,但拒绝响应
response.sendError(403, "Bad or missing token!");
return false;
}
if (token == null) {
token = UUID.randomUUID().toString();
CookieUtils.addCookie(request, response, "csrf_token", token);
}
//request只有两个页面之间的作用域,不能再传给第3个页面了
//request只能在一次页面传递之间保存数据,超过就会丢失
request.setAttribute("token", token);
return true;
}
}