XSS 小结

前言

上一篇文章中写了关于项目中处理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注入 等安全问题。

参考资料

freebuf.com XSS系列文章

CSDN WEB安全实战系列文章

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值