前言
上一篇文章中写了关于项目中处理SQL盲注
的安全漏洞.其中也提到了XSS
.这篇文章就来说一下XSS 是怎么来进行攻击的,如何对其进行防御.
概述
XSS攻击
:跨站脚本攻击(Cross Site Scripting),为不和层叠样式表(Cascading Style Sheets, CSS)的缩写混淆,故将跨站脚本攻击缩写为XSS
。
XSS
是一种经常出现在web应用中的计算机安全漏洞,它允许恶意web用户将代码植入到提供给其它用户使用的页面中。比如这些代码包括HTML代码和客户端脚本。攻击者利用XSS
漏洞旁路掉访问控制——例如同源策略(same origin policy)。这种类型的漏洞由于被黑客用来编写危害性更大的网络钓鱼(Phishing)攻击而变得广为人知。对于跨站脚本攻击,黑客界共识是:跨站脚本攻击是新型的“缓冲区溢出攻击“,而JavaScript是新型的“ShellCode”。
`XSS`攻击的危害包括:
1、盗取各类用户帐号,如机器登录帐号、用户网银帐号、各类管理员帐号
2、控制企业数据,包括读取、篡改、添加、删除企业敏感数据的能力
3、盗窃企业重要的具有商业价值的资料
4、非法转账
5、强制发送电子邮件
6、网站挂马
7、控制受害者机器向其它网站发起攻击
—-摘自百度百科 XSS攻击
原理
我们首先搭建一个简易的环境来模拟XSS
攻击 ,其中xssTest.jsp
代码如下
模拟XSS攻击实例 1
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Insert title here</title>
</head>
<body>
<form action="xssTestServltet" method="get">
<input type="txt" name="userName">
<input type="submit" value="提交">
</form>
<hr>
您输入的账户为: ${userName }
</body>
</html>
后台处理 xssTestServlter.java
@WebServlet("/xssTestServltet")
public class XssTestServltet extends HttpServlet{
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//接受参数
String userName=request.getParameter("userName");
//存储数据到 作用域
request.setAttribute("userName", userName!=null?userName:"");
//跳转到原页面.
request.getRequestDispatcher("/xssTest.jsp").forward(request, response);
}
}
然后我们请求到测试页面
我们尝试输入 qazxc110,得到的结果为:
此时页面源代码为:
我们注意到,我们的输入的字符被原封不动的显示在 15行的位置.那么我们试想一下,如果我们在输入框中输入 这样一段代码:<script>alert('xss')</script>
,会是什么样子.按照刚才的测试,那么这次源码中在刚才的位置就应该显示<script>alert('xss')</script>
这串代码相当于在展示的位置插入了一段javaScript 代码,那么就应该执行alert弹出弹窗 并打印 xss
.
我们按照刚才的假设,在代码中实验.我们输入<script>alert('xss')</script>
这串代码在文本框中,然后提交页面.然后我得到了一下页面:
这时我们看到浏览器出现了弹窗,说明刚才的那段script
代码执行了.这个时候也就基本说明了,页面存在XSS
漏洞了.
此时的页面源码为:
这是一种非常简单的方式.我们只是在页面上弹窗一个字符串而已,显然是执行了 <script></script>
中的代码.那么这里面不但可以写弹窗代码,还可以写其他比如让页面跳转到一个指定的地址.如果这个地址的页面是一个跟当前页面相似的钓鱼页面的话,后果不堪设想.
很多网站都有留言模块.我们可以在留言板上插入一段恶意的代码,提交到后台.由于这段代码是保存在服务器上,若管理员并没有去及时发现并删除这段留言的代码,当范文留言列表的时候,留言会被加载出来.当用户访问这个留言的页面时,这段恶意代码就会执行.实例如下:
模拟XSS攻击实例 2
/**
* 原页面访问地址.
*
*/
@WebServlet("/xssTestServltet")
public class XssTestServltet extends HttpServlet{
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//接受参数
String userName=request.getParameter("userName");
//存储数据到 作用域
request.setAttribute("userName", userName!=null?userName:"");
//模拟存储一个cookie 到 客户端.在xss 攻击链接中获取.
Cookie cookie = new Cookie("xxzx","1234");
response.addCookie(cookie);
//模拟数据库中存储的留言.
request.setAttribute("ly1", "xxzx");
//模拟数据库中存储的留言.这里插入了一段 script 代码.去访问src 对应的xss 攻击 链接.
request.setAttribute("ly2", "<script src='http://localhost:8080/XssTestServlet2'></script>么么哒");
//跳转到原页面.
request.getRequestDispatcher("/xssTest.jsp").forward(request, response);
}
}
/**
* 模拟留言中 xss 攻击的 地址.
*
*/
@WebServlet("/XssTestServlet2")
public class XssTestServlet2 extends HttpServlet{
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("访问了攻击地址.......");
//得到cookies
Cookie[] cookies = request.getCookies();
for (Cookie cookie : cookies) {
System.out.println("name:"+cookie.getName());
System.out.println("value:"+cookie.getValue());
}
}
}
这时我们任意用户在浏览器中访问 留言页面时,页面的代码:
在留言2
的后面,还去加载了一个 我们之前定义好的XSS
链接 .这样一来我们除了请求正常的地址,页面还另外请求了攻击的地址:
而在我们XSS 攻击地址的后台.我们获取到了这些信息:
当管理员进入后台来浏览留言时,就会触发,然后管理员的cookie 后后台地址 等等一系列信息都可以被拿到,然后攻击者就可以用一些cookie欺诈工具来更改管理员的cookie ,就可以不用输入账号,密码 也可以以管理员的身份登录进系统.
解决方案
为解决上面出现的问题,现提供两种解决方案.
方案 1
我们使用一个第三方的jar 包.调用第三方的api 来防止 XSS 攻击带来的危险.具体步骤如下:
首先,添加 apache 的commons-lang-2.6.jar .
然后,在后台调用 StringEscapeUtils.escapeHtml(string);
StringEscapeUtils.escapeJavaScript(string);
StringEscapeUtils.escapeSql(string);
对请求过来的参数进行转义,若还需要这些值到前台.我们可以使用;StringEscapeUtils.unescapeXXX(String)
;(XXX
可以表示 Html
,JavaScript
,Sql
,Xml
等) 来进行反转义,或者在前台使用js 调用escape 可是可以的.
后台处理action:
@RequestMapping("/stringEscapeUtils")
public void stringEscapeUtils(HttpServletRequest request,HttpServletResponse response){
String str=request.getParameter("username");
System.out.println("username: "+str);
System.out.println("escapeSql: "+StringEscapeUtils.escapeSql(str));
System.out.println("escapeHtml: "+StringEscapeUtils.escapeHtml(str));
System.out.println("escapeJavaScript: "+StringEscapeUtils.escapeJavaScript(str));
}
当我们前台输入:<script>alert('1')</script>
时,后台的输出结果为:
注意:commons-lang-3.x
里面已经没有 escapeSql
escapeJavaScript
方法,取而代之的是 escapeEcmaScript
.具体的使用方法,就请自行验证了.
方案 2
这里我们重点说一下方案二.这也是我们解决XSS
攻击时采用的一种方式.
前面我们说到 XSS
攻击无非就是我们对于参数的过滤没有做好.导致一些不安全的字符被原封不动的进入到我们的业务中.那么最终就是对参数进行过滤.在这种方案中.我们需要自己写一个Filter
,使用Filter
来过滤发出的请求.对于每个post 的请求的参数过滤一些关键字,把他们替换成安全的.例如:< > ' " \ / # &
,方法是实现一个自定义的 HttpServletRequestWrapper
,然后在Filter
里面调用它替换getParameter
方法即可,具体步骤如下.
首先 我们在后台添加一个 XssHttpServletRequestWrapper
类,让它继承 javax.servlet.http.HttpServletRequestWrapper
,代码如下:
package com.zhouq.filter.Xss;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
/** 对请求 的参数 进行 Xss 安全过滤
* @author zhouq
*
*/
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
public XssHttpServletRequestWrapper(HttpServletRequest request) {
super(request);
}
/**
* 替换 getParameterValues
*/
public String[] getParameterValues(String parameter) {
String[] values = super.getParameterValues(parameter);
if (values == null) {
return null;
}
int count = values.length;
String[] eccodedValues = new String[count];
// 对每个参数值进行 XSS 清理
for (int i = 0; i < count; i++) {
eccodedValues[i] = cleanXSS(values[i]);
}
return eccodedValues;
}
/**
* 替换 getParameter
*/
public String getParameter(String parameter){
String value=super.getParameter(parameter);
if(value!=null){
//对参数值进行 XSS 清理
value=cleanXSS(value);
}
return value;
}
public String getHeader(String name) {
String value = super.getHeader(name);
if (value == null)
return null;
return cleanXSS(value);
}
/** XSS 清理
* @param value
* @return
*/
private String cleanXSS(String value) {
System.out.println("清理前 :"+value);
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", "");
System.out.println("清理后 :"+value);
System.out.println("----------------------------------------------------------");
return value;
}
}
然后我们还需要在后台添加一个过滤器 XssFilter
,让它实现 javax.servlet.Filter
,具体代码如下:
package com.zhouq.filter.Xss;
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 {
FilterConfig config = null;
@Override
public void destroy() {
this.config = null;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
//执行 我们自己定义的 XssHttpServletRequestWrapper
chain.doFilter(new XssHttpServletRequestWrapper((HttpServletRequest) request), response);
}
@Override
public void init(FilterConfig config) throws ServletException {
this.config = config;
}
}
最后,我们还需要在web.xml 中应用 这个过滤器.web.xml 中的配置如下:
<!-- 配置自实现的 XSS 过滤器 -->
<filter>
<filter-name>XssFilter</filter-name>
<filter-class>com.zhouq.filter.Xss.XssFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>XssFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
到这里.我们的 XSS
清理就编写完成了.每一次请求中的危险字符,敏感信息都会被过滤掉,您可以去测试一下是否对您的应用生效.当然了,如果您要处理更多的字符,那么您可以继续在 cleanXSS
中去添加你需要处理的东西.当然,需要注意的是,一些必要的字符不能被过滤,否则就改变了用户的真实数据.比如在我们当前系统中在为菜单添加操作的时候 会有前缀为javascript
的代码.那么这里就会被过滤掉.如果你应用中也出现这种情况,那么就得请您自己做取舍了.
假设登录页面有个输入用户名和密码的输入框,可以有很多 XSS
CSRF 注入钓鱼网站
SQL
注入等的攻击手段,例如:
输入用户名 : >"'><script>alert(1779)</script>
输入用户名: usera>"'><img src="javascript:alert(23664)">
输入用户名: "'><IMG SRC="/WF_XSRF.html--end_hig--begin_highlight_tag--hlight_tag--">
输入用户名: usera'"><iframe src="http://demo.testfire.net--en--begin_highlight_tag--d_highlight_tag--">
密码随意输入。
下面提供了一个登录页面的攻击实例,你可以通过下面的方式进行简单的测试:
用户名: `1' or '1'='1`
密码:`<script>alert('xss')</script>`
测试结果:
结束语
到这里,XSS
攻击的防御措施就写完了,使用 Filter
的这种方式,不仅仅可以防御 XSS
攻击,还可以防御 CSRF
攻击,SQL注入
等安全问题。