JAVA SSM框架常见安全风险分析

admin 2022年5月11日06:35:41评论39 views字数 8486阅读28分17秒阅读模式

0x00 简介

SSM 即 SPRING、SPRINGMVC、MYBATIS 三大框架的整合,随着其开发带来的各种便利以及好处,越来越多的公司以及RD使用其开发各种线上产品和后台管理系统,但也带来了许多安全性的问题。

虽然JAVA 本身规范化程度比较高,按照框架的标准进行开发确实能减少许多的SQL注入问题,但在其他方面基本上是没有做任何安全考虑。使用SSM 通过MYBATIS的配置生成SQL语句,基本能屏蔽掉大部分的SQL注入问题。但由于表单提交等大多使用POJO和DTO绑定的方式,虽然SPRINGMVC自带有个编码处理方法,但由于使用有限,String类型参数不会有任何的安全性处理,这样大片的XSS漏洞随之产生 ,CSRF 漏洞上,SRPINGMVC亦没有内置的接口。其他的逻辑产生的安全问题就不再这里面的分析范畴内了。

0x01 SQL注入问题

MYBATIS支持配置XML查询和通用MAPPER查询两种。使用配置XML的方式,XML 文件中,可以使用两种符号接收#和$,#符号类似于参数绑定的方式也就是JAVA的预编译处理,这样是不会带来SQL注入问题的;而$却相当于只把值传进来,不做任何处理,类似于拼接SQL语句。

测试使用相关代码如下:

  • CONTROLLER代码

  1. @RequestMapping("/get1")

  2.    public String username1(HttpServletRequest request, Model model) {

  3.        String name = request.getParameter("name");

  4.        User user = this.userserivce.getUserByUsername1(name);

  5.        model.addAttribute("user", user);

  6.        return "showuser";

  7.    }

  8.    @RequestMapping("/get2")

  9.    public String username2(HttpServletRequest request, Model model) {

  10.        String name = request.getParameter("name");

  11.        User user = this.userserivce.getUserByUsername2(name);

  12.        model.addAttribute("user", user);

  13.        return "showuser";

  14.    }

  • DAO代码

  1. User selectByUsername1(String name);

  2. User selectByUsername2(@Param("name") String name);

  • MYBATIS XML 配置

  1. <select id="selectByUsername1" resultMap="BaseResultMap" parameterType="java.lang.String" >

  2.    select

  3.    <include refid="Base_Column_List" />

  4.    from user

  5.    where user_name = #{name,jdbcType=VARCHAR}

  6.  </select>

  7.  <select id="selectByUsername2" resultMap="BaseResultMap" parameterType="java.lang.String" >

  8.    select

  9.    <include refid="Base_Column_List" />

  10.    from user

  11.    where user_name = '${name}'

  12.  </select>

1、测试使用#传参

正常访问,如图:

JAVA SSM框架常见安全风险分析

JAVA SSM框架常见安全风险分析

JAVA SSM框架常见安全风险分析

加入payload访问,如图:

JAVA SSM框架常见安全风险分析

无查询结果,如图:

JAVA SSM框架常见安全风险分析

JAVA SSM框架常见安全风险分析

可以看到数据执行的时候已经做了转义的处理。

2、测试使用$传参

正常访问,如图:

JAVA SSM框架常见安全风险分析

JAVA SSM框架常见安全风险分析

可以看到是拼接的SQL语句,加入payload测试访问,如图:

JAVA SSM框架常见安全风险分析

依然正常获取了查询结果, 已有SQL注入问题。

JAVA SSM框架常见安全风险分析

在开发的过程中咱们尽量使用#传参,减少$传参的使用,如有需要,也注意下输入参数的转义处理。

0x02 XSS问题

测试使用相关代码如下:

  • CONTROLLER

  1. @RequestMapping("/show")

  2.    public String show(HttpServletRequest request){

  3.        return "xssshow";

  4.    }

  5.    @RequestMapping("get")

  6.    public String get(HttpServletRequest request, Model model){

  7.        Integer id = Integer.parseInt(request.getParameter("id"));

  8.        User user = this.userserivce.getUserById(id);

  9.        model.addAttribute("user", user);

  10.        return "getuser";

  11.    }

  12.    @RequestMapping("/add")

  13.    public String add(User user){

  14.        int i = this.userserivce.insert(user);

  15.        return "redirect:/xsstest/show";

  16.    }

插入payload测试 toor"'><svg/onload=alert(/xss/)>,数据库里可看到并未做任何处理,如图:

JAVA SSM框架常见安全风险分析

查询访问,存在存储型XSS漏洞,如图:

JAVA SSM框架常见安全风险分析

1.一些安全的措施

SPRINGMVC和JSTL进行编码处理

  • WEB.XML中添加

  1.    <context-param>

  2.        <param-name>defaultHtmlEscape</param-name>

  3.        <param-value>true</param-value>

  4.    </context-param>

  • JSP模板中添加,分为以下三种情况:

针对所有FORM,如下:

  1. <spring:htmlEscape defaultHtmlEscape="true" />

针对单个INPUT等,如下:

  1. <form:input path="name" htmlEscape="true"/>

直接输出的标签,如下:

   <c:out value="${user.userName}" />

重写HttpServletRequestWrapper或HttpServletResponseWrapper使用FILTER进行过滤

  • WEB.XML中添加

  1. <filter>

  2.        <filter-name>XSS</filter-name>

  3.        <filter-class>ssm.sec.filter.XSSFilter</filter-class>

  4.    </filter>

  5.    <filter-mapping>

  6.        <filter-name>XSS</filter-name>

  7.        <url-pattern>/*</url-pattern>

  8.    </filter-mapping>

  • RequestWrapper.java

  1.    private static Logger logger = Logger.getLogger(RequestWrapper.class);

  2.    public RequestWrapper(HttpServletRequest servletRequest) {

  3.        super(servletRequest);

  4.    }

  5.    public String[] getParameterValues(String parameter) {

  6.        logger.info("InarameterValues .. parameter .......");

  7.        String[] values = super.getParameterValues(parameter);

  8.        if (values == null) {

  9.            return null;

  10.        }

  11.        int count = values.length;

  12.        String[] encodedValues = new String[count];

  13.        for (int i = 0; i < count; i++) {

  14.            encodedValues[i] = cleanXSS(values[i]);

  15.        }

  16.        return encodedValues;

  17.    }

  18.    public String getParameter(String parameter) {

  19.        logger.info("Inarameter .. parameter .......");

  20.        String value = super.getParameter(parameter);

  21.        if (value == null) {

  22.            return null;

  23.        }

  24.        logger.info("Inarameter RequestWrapper ........ value .......");

  25.        return cleanXSS(value);

  26.    }

  27.    public String getHeader(String name) {

  28.        logger.info("Ineader .. parameter .......");

  29.        String value = super.getHeader(name);

  30.        if (value == null)

  31.            return null;

  32.        logger.info("Ineader RequestWrapper ........... value ....");

  33.        return cleanXSS(value);

  34.    }

  35.    private String cleanXSS(String value) {

  36.        logger.info("InnXSS RequestWrapper ..............." + value);

  37.        value = value.replaceAll("eval\((.*)\)", "");

  38.        value = value.replaceAll("[\"\'][\s]*javascript:(.*)[\"\']", """");

  39.        value = value.replaceAll("(?i)<script.*?>.*?<script.*?>", "");

  40.        value = value.replaceAll("(?i)<script.*?>.*?</script.*?>", "");

  41.        value = value.replaceAll("(?i)<.*?javascript:.*?>.*?</.*?>", "");

  42.        value = value.replaceAll("(?i)<.*?\s+on.*?>.*?</.*?>", "");

  43.        value = value.replaceAll("<", "& lt;").replaceAll(">", "& gt;");

  44.        value = value.replaceAll("\(", "& #40;").replaceAll("\)", "& #41;");

  45.        value = value.replaceAll("'", "& #39;");

  46.        logger.info("OutnXSS RequestWrapper ........ value ......." + value);

  47.        return value;

  48.    }

  49. }

  • XSSFilter.java

  1. package ssm.sec.filter;

  2. import java.io.IOException;

  3. import javax.servlet.Filter;

  4. import javax.servlet.FilterChain;

  5. import javax.servlet.FilterConfig;

  6. import javax.servlet.ServletException;

  7. import javax.servlet.ServletRequest;

  8. import javax.servlet.ServletResponse;

  9. import javax.servlet.http.HttpServletRequest;

  10. import org.apache.log4j.Logger;

  11. public class XSSFilter implements Filter {

  12.    private static Logger logger = Logger.getLogger(XSSFilter.class);

  13.    private FilterConfig filterConfig;

  14.    public void init(FilterConfig filterConfig) throws ServletException {

  15.        this.filterConfig = filterConfig;

  16.    }

  17.    public void destroy() {

  18.        this.filterConfig = null;

  19.    }

  20.    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)

  21.        throws IOException, ServletException {

  22.        logger.info("Inlter CrossScriptingFilter ...............");

  23.        chain.doFilter(new RequestWrapper((HttpServletRequest) request), response);

  24.        logger.info("Outlter CrossScriptingFilter ...............");

  25.    }

  26. }

重新插入数据, 会发现已经进行了过滤处理,如图:

JAVA SSM框架常见安全风险分析

数据库查看都是过滤和转义的结果,如图:

JAVA SSM框架常见安全风险分析

当然有时候由于业务需要,可能允许用户输入这种危险的字符,这种通用的并不适用,但可以考虑把一些特殊的符号转义成中文符号,也许也恰好能满足业务的需求,如< 转义成 <等,看起来和英文的一样,但由于解释器并不识别, 而又变成安全的了。

SPRINGMVC 视图中传递变量一般使用EL表达式

虽然EL中内置许多默认的函数,但并没有编码处理的函数,因此只能够自定义函数进行处理。

  • 开发函数处理类,处理类就是普通的类,每个函数对应类中的一个静态方法

  1. public class XSSEL {

  2.    private static Logger logger = Logger.getLogger(XSSEL.class);

  3.    public static String HTMLEncode(String val){

  4.        logger.info("In EL func values ====>"+ val);

  5.        if(val != null){

  6.            val = val.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;").replace(""", "&quot;")

  7.                    .replace("/", "&#x2F;").replace("'", "&#x27;");

  8.            logger.info("Out EL func values ====>"+ val);

  9.            return val;

  10.        }else{

  11.            return "";

  12.        }

  13.    }

  14. }

  • 建立TLD文件,定义表达式函数

  1. <?xml version="1.0" encoding="UTF-8"?>

  2. <taglib xmlns="http://java.sun.com/xml/ns/j2ee"

  3.    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

  4.    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee

  5.    http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"

  6.    version="2.0">

  7.  <tlib-version>1.0</tlib-version>

  8.  <short-name>sec</short-name>

  9.  <function>

  10.    <name>HTMLEncode</name>

  11.    <function-class>ssm.sec.utils.XSSEL</function-class>

  12.    <function-signature>java.lang.String HTMLEncode(java.lang.String)</function-signature>

  13.  </function>

  14. </taglib>

  • 在WEB.XML文件中配置(可省略)

  1.    <jsp-config>

  2.        <taglib>

  3.            <!-- 配置标签的引用地址 JSP页面中引用时使用 -->

  4.            <taglib-uri>/eltag</taglib-uri>

  5.            <!-- 配置标签的TLD文件地址 -->

  6.            <taglib-location>/WEB-INF/XSSEL.tld</taglib-location>

  7.        </taglib>

  8.    </jsp-config>

  • 在JSP页面内导入并且使用

  1. <%@ taglib uri="/eltag" prefix="sec" %>

  2. <%@ taglib uri="/WEB-INF/XSSEL.tld" prefix="sec" %>WEB.XML文件中不配置的情况

  3. ${sec:HTMLEncode(user.userName)}

针对复杂的业务也可以考虑定义一系列安全编码的方法,避免重复造轮子的同时也可以增强程序的安全性。

0x03 CSRF问题

由于SPRINGMVC 本身不提供像DJANGO那样的CSRF安全接口,因此在这方面就要脆弱了许多。这和其他框架的存在和处理的方式亦差不多,也或者说写出的CSRF代码也更具有通用性。所以呢主要是浅谈对CSRF问题的处理思路以及相应的好与坏。

1.验证HTTP REFERER字段

HTTP协议的REFERER字段会记录请求来源地址,在通常请况下,一般情况下安全请求来自于同一个网站,如果REFERER来源为第三方网站,则很可能是CSRF攻击。即使REFERER是来自于同一个网站,也有很能是站内的CSRF攻击,那么只能通过正则匹配出来源是否为我们指定的允许请求的链接。

2.添加验证码

CSRF攻击的过程是在用户不知情的情况下构造网络请求,所以验证码机制是最有效最快捷的方法。但很多时候,出于用户体验的情况下,并不能给所有的操作都添加上验证码,除非某个操作的敏感性、重要性都十分的高,那么可以考虑加上验证码的方式来验证。

3.添加Token值

token储存在cookie中

Token储存在cookie中,必须考虑两个问题:

1、假如同域名下存在XSS,那么cookie则可以被盗取;

2、表单的token值如果和cookie储存的相等,那么当cookie值被盗取的时候,则可以构造出表单cookie。

针对以上两个问题,那么在cookie储存token的实现过程中,必须的有相应的解决方法:

1、对cookie值储存token设置httponly;

2、设置httponly在某些情况下还是可以泄露cookie的,因此表单的token和cookie的token得有一个加密的过程。

token储存在session中

Token储存在session中,其实和cookie差不多是一个原理,但有点小差别的是session是储存在服务器的,因此诞生的一个问题便是在分布式环境中session不复制的话,那么此机制便失效了,并且会对正常的业务产生影响。

因此,当使用sesseion储存的时候,必须考虑搭建的web环境为非分布式环境。

one-time token(token储存在memcache等中)

One-time token即一次性token值,请求一次一页面即获取不同的token值。One-time token的难点在“并行会话”中,如用户在同一个站点当中打开两个一样的页面,那么必然两个表单内容也一样,CSRF防护措施不能导致只有一个页面才能提交表单,其它的表单都包含的是无效的token值。因此one-time token必须要在保证不影响“并行会话”的基础上实现。

以下是实现思路:

1、 生成token值,以此token值作为memcached的key和value储存;

2、在表单中加入此token值的hidden表单;

3、验证时比较表单和memcached的token值。

源代码 https://github.com/jige003/ssm-sec

原文始发于微信公众号(毕方安全实验室):JAVA SSM框架常见安全风险分析

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年5月11日06:35:41
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   JAVA SSM框架常见安全风险分析http://cn-sec.com/archives/901454.html

发表评论

匿名网友 填写信息