Java EE 开发框架安全审计——SSM框架(入门篇)

目录

前言

(一)SSM 框架简介

1、Spring MVC

2、Spring

3、MyBatis

4、Servlet

(二)执行流程和审计思路

(三)Spring 核心配置文件 applicationContext.xml

3.1 标签

3.2 标签

(四)SSM 之 Spring MVC 执行流程

(五)SSM 之 Spring 执行流程

(六)SSM 之 MyBatis 执行流程

 mybatis-config.xml

总结:

(七).审计的重点——filter 过滤器


前言

        SSM 框架,即 Spring MVC+Spring+MyBatis 3 个开源框架整合在一起的缩写。 SSM 框架之前,生产环境中多采用 SSH 框架(由 Struts2+Spring+Hibernate 3 个开源框架整合而成)。后因 Struts2 爆出众多高危漏洞,导致目前 SSM 逐渐代替 SSH 成为主流开发框架的选择。通过本次调程序,遇到了太多的问题,昨天晚上电脑自动的把IDEA关闭了,上午环境弄了半天。
        由于笔者个人水平有限,行文如有不当,还请各位师傅评论指正,非常感谢!

(一)SSM 框架简介


1、Spring MVC

Java代码审计前置知识——SpringMVC基础__Cyber的博客-CSDN博客

Spring MVC 是一种基于 Java 实现的 MVC 设计模式的请求驱动类型的轻量级Web 框架,采用 MVC 架构模式的思想,将 Web 层进行职责解耦。基于请求驱动指的是使用请求- 响应模型,该框架的目的是简化开发过程

2、Spring

Java代码审计前置知识——Spring框架AOP和IoC__Cyber的博客-CSDN博客

Spring 是分层的 Java SE/EE full-stack 轻量级开源框架,以 IoC Inverse of Control,控制反转)和 AOP Aspect Oriented Programming ,面向切面编程)为内核,使用基本的 JavaBean 完成以前只可能由 EJB 完成的工作,取代了 EJB 臃肿和低效的开发模式。Spring 的用途不仅仅限于服务器端的开发。从简单性、可测试性和松耦合性角度而言,绝大部分 Java 应用可以从 Spring 中受益。

3、MyBatis

Java代码审计前置知识——MyBatis__Cyber的博客-CSDN博客

MyBatis 是支持定制化 SQL 、存储过程以及高级映射的优秀的持久层框架。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。 MyBatis可以对配置和原生 Map 使用简单的 XML 或注解,将接口和 Java POJO Plain Old Java Object,普通的 Java 对象)映射成数据库中的记录

4、Servlet

java web 前置知识——Servlet(一)__Cyber的博客-CSDN博客

基于Jsp和Servlet的简单项目__Cyber的博客-CSDN博客_jsp+servlet项目

Spring MVC 的底层就是以 Servlet 技术进行构建的。 Servlet 是基于 Java 技术的Web 组件,由容器管理并产生动态的内容。 Servlet 与客户端通过 Servlet 容器实现的请求/ 响应模型进行交互。

(二)执行流程和审计思路


代码审计的核心思想是追踪参数,而追踪参数的步骤就是程序执行的步骤。因此,代码审计是一个跟踪程序执行步骤的过程,了解了 SSM 框架的执行流程自然会了解如何如跟踪一个参数,剩下的就是观察在参数传递的过程中有没有一些常见的漏洞点。
Java EE 开发框架安全审计——SSM框架(入门篇)_第1张图片 图 2-1 图书管理程序的目录结构
这里通过创建一个简单的 Java代码审计前置知识——SSM整合__Cyber的博客-CSDN博客 来描述基于SSM 框架搭建的项目完成用户请求的具体流程,以及观察程序对参数的过滤是如何处理的图 -1 展示了 一个简单的图书管理程序的目录结构,主要功能是对图书名称的增、删、查、改

         无论是审计一个普通项目或者是 Tomcat 所加载 的项目,通常都从 web.xml 配置文件开始入手。Servlet 3.0 以上版本提供一些新注解来达到与配置 web.xml相同的效果。但是在实际项目中主流的配置方法仍然是 web.xml

        web.xml 文件的主要工作包括以下几个部分:
  • web.xml 启动 Spring 容器。
  •  DispathcherServlet 的声明。
  •  其余工作是 session 过期、字符串编码等。
        首先是生成 DispatcherServlet 类。 DispatcherServlet是前端控制器设计模式的实现,提供 Spring Web
MVC 的集中访问点(也就是把前端请求分发到目标Controller),而且与 Spring IoC 容器无缝集成,从而
可以利用 Spring 的所有优点。
        简单地理解就是,将用户的请求转发至 SpringMVC 中,交由 Spring MVC Controller 进行更多处理。
        子标签是生成DispatcherServlet时的初始化参数 contextConfigLocation,Spring 会根据该参数加载所有逗号分隔的 xml 文件。如果没有这个参数, Spring 默认加载WEB-INF/DispatcherServlet-servlet.xml 文件。
        如下代码所示,标签中还有一个子标签,其中 value
是“/”代表拦截所有请求。其 中还包含 标签,具体功能会在后面进行介绍



    
    
        DispatcherServlet
        org.springframework.web.servlet.DispatcherServlet
        
            contextConfigLocation
            classpath:applicationContext.xml
        
        1
    
    
        DispatcherServlet
        /
    

    
    
        encodingFilter
        
            org.springframework.web.filter.CharacterEncodingFilter
        
        
            encoding
            utf-8
        
    
    
        encodingFilter
        /*
    

    
    
        15
    

(三)Spring 核心配置文件 applicationContext.xml


我们可以根据加载顺序查看 applicationContext.xml ,如下代码 所示;


    
    
    
applicationContext.xml 中包含 3 个配置文件,它们是 Spring 用来整合 Spring MVC和MyBaits 的配置文件,文件中的内容都可以直接写入 applicationContext.xml 中,因为 applicationContext.xml Spring 的核心配置文件,例如生成 Bean ,配置连接池,生成 sqlSessionFactory 。但是为了便于理解,这些配置分别写在 3 个配置文件中,由
applicationContext.xml 3 xml 进行关联。由图 3 -1  我们可以清晰地看到
applicationContext.xml 将这 3 个配置文件关联了起来
Java EE 开发框架安全审计——SSM框架(入门篇)_第2张图片 图 3-1 applicationContext.xml 关联 3 个配置文件

        数据经由 DispatcherServlet 派发至 Spring-mvc.xml Controller 层。我们先看
Spring-mvc.xml 配置文件,如下代码 所示



    

    


    
        
        
        
    

    

3.1 标签


如果在 web.xml servlet-mapping url-pattern 设置的是 / ,而不是 .do(之前编程风格) ,表示将所有的文件包含静态资源文件都交给 Spring MVC 处理,这时需要用到
。如果不加,则 DispatcherServlet 无法区分请求是资源文件还是 MVC 的注解,而导致 Controller 的请求报 404 错误(内部自动写好的,感兴趣的可以查看源码)

3.2 标签


Spring-mvc.xml 中配置 后,会在 SpringMVC 上下文中定义一个 org.springframework.web.servlet.resource.DefaultServletHttp-RequestHandler ,它会像检查员一样对进入 DispatcherServlet URL 进行筛查。如果是静态资源的请求,就将该请求转由 Web 应用服务器默认的 Servlet 处理;如果不是静态资源的请求,则交由 DispatcherServlet 继续处理
        其余两项之一是指定了返回的 view 所在的路径,另一个是指定 Spring MVC 注解的扫描路径,可以发现该配置文件中都是与 Spring-mvc 相关的配置。

(四)SSM 之 Spring MVC 执行流程


接下来就是 Spring MVC Controller 层接受前台传入的数据。以下通过 demo  运行以方便演示和讲解,首页如图 4 -1  所示
Java EE 开发框架安全审计——SSM框架(入门篇)_第3张图片 图 4-1

查看首页的页面源码


点击进入列表页

可以看到 a 标签的超链接是 http://localhost:8080/SSMFrameWorkTest_war/book/allbook。
        ${pageContext.request.contextPath} JSP 取得绝对路径的方法 , 也就是取出部署的应用程序名或者是当前的项目名称,避免在把项目部署到生产环境中时出错。
        此时后台收到的请求路径为/book/allBook Spring MVC 在项目启动时会首先去扫描我们指定的路径,即 com.xiaowei.controller 路径下的所有类。 BookController类的部分代码如下 所示:
package com.xiaowei.controller;

import com.xiaowei.pojo.Books;
import com.xiaowei.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.List;

@Controller
@RequestMapping("/book")
public class BookController {
    // controller 调 service 层
    @Autowired
    @Qualifier("BookServiceImpl")
    private BookService bookService;

    // 查询全部书籍,并且返回到一个书籍展示页面
    @RequestMapping("/allBook")
    public String list(Model model) {
        List list = bookService.queryAllBook();
        model.addAttribute("list", list);
        return "allBook";
    }
}
Spring MVC 会扫描该类中的所有注解,看到 @Controller 时会生成该 Controller的 Bean ,扫描到 @RequestMappting 注解时会将 @RequestMappting 中的 URI 与下面的方法形成映射。所以我们请求的 URI 是“ /book/allBool ”, Spring MVC 会将数据交由 BookController 类的 list 方法来处理。
        仔细观察 list 方法,其中调用了 bookService 参数的 queryAllBook 方法,这里使用了两个注解:@Autowired @Qualifier 。这两个注解的作用简单介绍如下:
0x01 @Autowired

此注解的作用:自动按照类型注入,只要有唯一的类型匹配就能注入成功,传的类型不唯一时则会报错。

0x02 @Qualifier


该注解的作用:在自动按照类型注入的基础上,再按照 bean id 注入。它在给类成员注入数据时不能独立使用;但是在给方法的形参注入数据的时候,可以独立使用。
        由此可以看到 bookService 参数的类型是 BookService 类型,通过注解自动注入的 Bean id 叫作 BookServiceImpl

(五)SSM 之 Spring 执行流程


这里我们就要从 Spring MVC 的部分过渡到 Spring 的部分,所谓的过渡就是我们 从 Spring MVC Controller 层去调用 Service 层,而 Service 层就是我们使用 Spring进行 IoC 控制和 AOP 编程的地方。

        首先我们需要查看配置文件 spring-service.xml,如下代码所示:



    
    
    
    

    
    
        
    

    
    
        
        
    

这里我们发现 id BookServiceImpl bean ,该 bean class 路径是 com.xiaowei.service.BookServiceImpl。 这个标签涉及 Spring 一大核心功能点,即 IoC 。本来编写一个项目需要我们自己手动去创建一个实例,在使用了 Spring 以后只需要生成的那个类的绝对路径,以及创建一个实例时需要传入的参数。传入参数的方法可以是通过构造方法,也可以通过 set 方法。用户还可以为这个 bean 设置一个名称方便调用(如果不设置 id 参数名,则 bean 的名称默认为类名开头的小写字母,比如说BookServiceImpl,如不特别指定,则生成的 bean 的名称是 bookServiceImpl)。Spring会在启动时将用户指定好的类生成的实例放入 IoC 容器中供用户使用。通俗地说就是本来由用户手动生成实例的过程交由 Spring 来处理,这就是所谓的IoC
      首先看到该类实现了 BookService 接口,查看该接口,如下代码所示:
package com.xiaowei.service;

import com.xiaowei.pojo.Books;

import java.util.List;

public interface BookService {
    // 增加一个Book
    int addBook(Books book);
    //根据id删除一个Book
    int deleteBookById(int id);
    //更新Book
    int updateBook(Books books);
    //根据id查询,返回一个Book
    Books queryBookById(int id);
    //查询全部Book,返回list集合
    List queryAllBook();
}
可以看到该接口中定义了 4 种方法,为了方便理解,这些方法的名字对应着日常项目中常用的操作数据库的 4 个方法,即增、删、改、查
        接下来查看接口的实现类 BookServiceImpl ,如下代码 所示:
package com.xiaowei.service;


import com.xiaowei.DAO.BookMapper;
import com.xiaowei.pojo.Books;

import java.util.List;

public class BookServiceImpl implements BookService{
    //调用dao层的操作,设置一个set接口,方便Spring管理
    private BookMapper bookMapper;

    public void setBookMapper(BookMapper bookMapper) {
        this.bookMapper = bookMapper;
    }

    public int addBook(Books book) {
        return bookMapper.addBook(book);
    }

    public int deleteBookById(int id) {
        return bookMapper.deleteBookById(id);
    }

    public int updateBook(Books books) {
        return bookMapper.updateBook(books);
    }

    public Books queryBookById(int id) {
        return bookMapper.queryBookById(id);
    }

    public List queryAllBook() {
        return bookMapper.queryAllBook();
    }

}
实现了 BookService 接口,自然也需要实现该接口下的所有方法,找到 queryAllBook 方法,发现 queryAllBook 调用了 bookMapper 参数的 queryAllBook 方法,而 bookMapper BookMapper 类型的参数。
回过头来查看 spring-service.xml 中的配置。前面介绍了这一配置是将BookServiceImpl 类生成一个 bean 并放入 Spring IoC 容器中。 标签的意思是通过该类提供的 set 方法在 bean 生成时向指定的参数注入 value name 属性就是指定的参数的名称。可以看到 BookServiceImpl 中确实有一个私有参数,名为bookMapper(图5-1),并且提供了该属性的 set 方法。 ref 属性是指要注入的 value 是其他的Bean 类型,如果传入的是一些基本类型或者 String 类型,则不需要使用 ref ,只需将 ref 改成 value(图 5-2 )
Java EE 开发框架安全审计——SSM框架(入门篇)_第4张图片 图 5-1

图 5-2 

 这里通过 ref 属性向 BookServiceImpl 类中的 bookMapper 参数注入了一个 value,这个 value 是一个其他的 bean 类型,该 bean id bookMapper。此时 Service 层的

BookServiceImpl queryAllBook 方法的实现方式其实就是调用了 id bookMapper的 bean queryAllBook 方法,因此这个 id bookMapper bean 就是程序执行的下一步

(六)SSM 之 MyBatis 执行流程


接下来就是 Web 三层架构的数据访问层,也就是 MyBaits 负责的部分,通常这一部分的包名叫作 xxxdao ,也就是开发过程中经常提及的 DAO 层,该包下面的类和接口通常叫作 xxxDao 或者 xxxMapper 。此时用户的请求将从 Spring 负责的业务层过渡到 MyBatis 负责的数据层,但是 MyBaits Spring 之间不像 Spring MVC 和 Spring一样可以无缝衔接,所以我们需要通过配置文件将 MyBatis 与 Spring 关联起来。这里我们来查看一下 pom.xml,如下代码所示:
        
        
            org.mybatis
            mybatis
            3.5.2
        
        
            org.mybatis
            mybatis-spring
            2.0.2
        
可以看到我们导入的包除了 MyBatis 本身,还导入了一个 mybatis-spring 包,目的就是为了整合 MyBatis Spring spring-dao.xml 是用来整合 Spring MyBatis 的配置文件。
刚才我们看到 Spring 启动加载 bean 时会注入一个 id bookMapper bean ,但是我们并未在之前的任何配置文件包括注解中看到这个 bean 的相关信息,所以我们接下来要查看 spring-dao.xml 中有没有与这个 bean 有关的信息,如下代码 所示:



 
    
    
    
 
    
    
    
        
        
        
        
        
 
        
        
        
        
        
        
        
        
        
    
 
    
    
        
        
        
        
    
 
    
    
    
        
        
        
        
    
 
这里关联了一个 properties 文件,如下代码所示,里面是连接数据库和配置连接池时需要的信息
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssmbuild?useSSL=true&useUnicode=true&characterEncoding=utf8
jdbc.username=root
jdbc.password=123456
重点查看这个配置,如下代码 所示:

    
        
        
        
        
    
该配置通过生成 MapperScannerConfigurer bean 来实现自动扫描 com.xiaowei.dao 下面的接口包,然后动态注入 Spring IoC 容器中,同样动态注入的 bean 的 id 默认为类名(开头字母小写),目录下包含的文件如图 6-1   所示

Java EE 开发框架安全审计——SSM框架(入门篇)_第5张图片 图 6-1 

我们看到有一个叫作 BookMapper 的接口文件,说明之前生成 BookServiceImpl这个 bean 是通过 BookServiceImpl 类中的 setBookMapper() 方法)注入的 bookMapper,是由我们配置了 MapperScannerConfigurer 这个 bean 后,这个 bean 扫描 dao 包下的接口文件并生成 bean 。然后再注入 Spring IoC 容器中,所以我们才可以在 BookServiceImpl 这个 bean 中通过 标签注入 bookmapper 这个 bean
public void setBookMapper(BookMapper bookMapper) { 
this.bookMapper = bookMapper; 
}
然后查看该配置,如下代码 所示
  
    
        
        
        
        
    
这里生成一个 id SqlSessionFactory bean ,涉及 MyBatis 中的两个关键对象即SqlSessionFactory SqlSession
SqlSessionFactory

SqlSessionFactory 是 MyBatis 的关键对象,它是单个数据库映射关系经过编译后的内存镜像。SqlSessionFactory 对象的实例可以通过 SqlSessionBuilder 对象获得,而 SqlSessionBuilder 则可以从 xml 配置文件或一个预先定制的 Configuration 的实例构建出 SqlSessionFactory 的实例。SqlSessionFactory 是创建 SqlSession 的工厂。

SqlSession

SqlSession 是执行持久化操作的对象,类似于 JDBC 中的 Connection 。它是应用程序与持久存储层之间执行交互操作的一个单线程对象。SqlSession 对象完全包括以数据库为背景的所有执行 SQL 操作的方法,它的底层封装了 JDBC 连接,可以用SqlSession 实例来直接执行已映射的 SQL 语句。
MyBatis 框架主要是围绕着 SqlSessionFactory 进行的,实现过程大概如下:

  • 定义一个 Configuration 对象,其中包含数据源、事务、mapper 文件资源以及影响数据库行为属性设置 settings
  • 通过配置对象,则可以创建一个 SqlSessionFactoryBuilder 对象。
  • 通过 SqlSessionFactoryBuilder 获得 SqlSessionFactory 的实例。
  • SqlSessionFactory 的实例可以获得操作数据的 SqlSession 实例,通过这个实例对数据库进行。
        如果是 Spring MyBaits 整合之后的配置文件,一般以这种方式实现SqlSessionFactory 的创建,示例代码如下:
 
 
  
}
SqlSessionFactoryBean 是一个工厂 Bean ,根据配置来创建 SqlSessionFactory。 手动创建 SqlSessionFactory SqlSession 的流程如图 6 -2  所示
Java EE 开发框架安全审计——SSM框架(入门篇)_第6张图片 图 6-2 手动创建 SqlSessionFactory 和 SqlSession 的流程

 mybatis-config.xml


我们同时注意到 标签的 value 属性是“ classpath:mybatis-config.xml ”,如图 6-3  所示。这里又引入了一个 xml 配置文件,即 mybatis-config.xml ,是 MyBatis 的配置文件。
Java EE 开发框架安全审计——SSM框架(入门篇)_第7张图片 图 6-3 标签的 value 属性

 程序刚才执行到 BookServiceImpl 类的 queryAllBook 方法,然后该方法又调用bookMapper 的 queryAllBook 方法。我们发现 bookMapper 的类型是 BookMapper,并且从 sping-dao.xml 的配置文件中看到了该文件位于 com.xiaowei.dao 路径下。现在打开 BookMapper.java 文件进行查看代码:

package com.xiaowei.DAO;
import com.xiaowei.pojo.Books;
import org.apache.ibatis.annotations.Param;

import java.util.List;

public interface BookMapper {
    //增加一个Book
    int addBook(Books book);

    //根据id删除一个Book
    int deleteBookById(@Param("bookId") int id);

    //更新Book
    int updateBook(Books books);

    //根据id查询,返回一个Book
    Books queryBookById(@Param("bookId") int id);

    //查询全部Book,返回list集合
    List queryAllBook();
}
我们注意到这只是一个接口,众所周知,接口不能进行实例化,只是提供一个规范,因此这里的问题是调用的 BookMapper queryAllBook 是怎样执行?
        不难想到我们通过xml文件进行实例化操作,虽然我们对 MyBatis 的了解并不多,但是可以大概了解为什么BookMapper 明明只是接口,我们却可以实例化生成 BookMapper 的 bean,并且可以 调用它的方法呢?
        BookMapper.java 和 BookMapper.xml 显然不是 MyBatis 的全部,两个文件之间此时除了名字相同以外还没有什么直接联系,所以我们还需要将它们关联起来查看 mybatis-config.xml 的配置文件,如下代码 所示:
        
        
            
        

        
            
        
        可以发现 标签的 resource 属性的 value 就是 BookMapper.xml 的路径MyBatis,是基于 SQL 映射配置的框架。 SQL 语句都写在 Mapper 配置文件中,构建 SqlSession 类后,需要去读取 Mapper 配置文件中的 SQL 配置。而 标签就是用来配置需要加载的 SQL 映射配置文件路径的。
        也就是说,最终由 Spring 生成 BookMapper 的代理对象,然后由 MyBaits 通过标签将 BookMapper 代理对象中的方法与 BookMapper.xml 中的配置进行一一映射,并最终执行其中的 SQL 语句。
        可以发现此次请求最终调用了 BookMapper queryAllBook 方法,这时我们需要去 BookMapper.xml 中寻找与之对应的 SQL 语句,如下代码 所示:
 
我们看到最后执行的 SQL 语句如下:
SELECT * from ssmbuild.books

总结:

        至此我们的请求已经完成,从一开始的由 DispatcherServlet 前端控制器派发给Spring MVC,并最终通过 MyBatis 执行我们需要对数据库进行的操作。
        生产环境的业务代码肯定会比这个 demo 复杂,但是整体的执行流程和思路并不会有太大的变化,所以审计思路也是如此。

(七).审计的重点——filter 过滤器


        Spring MVC 是构建于 Servlet 之上的,所以 Servlet 中的过滤器自然也可以使用,只不过不能配置在 spring-mvc.xml 中,而是要直接配置在 web.xml 中,因为它是属于Servlet 的技术。

        先看看重新web.xml里面的配置:

Java EE 开发框架安全审计——SSM框架(入门篇)_第8张图片 图 7-1 重新查看 web.xml 文件

         在前面的分析中将这两个 filter 进行了注释,因此这两个 filter 并没有生效。我们以下面的 filter-name XSSEscape filter 来进行研究。

首先,此时程序是没有 XSS 防护的,所以存在存储型 XSS 漏洞,我们来尝试存储型 XSS 攻击,如图 7-1  所示
Java EE 开发框架安全审计——SSM框架(入门篇)_第9张图片 图 7-1 尝试存储型 XSS 攻击

单击新增功能,如图 7-2  所示:
Java EE 开发框架安全审计——SSM框架(入门篇)_第10张图片 图 7-2 新增功能

 查看提交路径,如图 7-3 所示

图 7-3 查看提交路径

去后台寻找与之对应的方法,如图 7-4  所示

Java EE 开发框架安全审计——SSM框架(入门篇)_第11张图片 图 7-4 寻找与之对应的方法

 找到后在这里设置断点,查看传入参数的详细信息,如图 7-5 所示

Java EE 开发框架安全审计——SSM框架(入门篇)_第12张图片 图 7-5 设置断点

 此时可以在 web.xml 中配置防御 XSS 攻击,如下代码所示;


    XSSEscape
    com.xiaowei.filter.XssFilter
    
        XSSEscape
        /*
        REQUEST
    
        这里声明了com.xiaowei.filter 的包路径下又一个类 XssFilter ,它是一个过滤器。
        下面的 属性中的 REQUEST 的意思是只要发起的操作是一次 HTTP请求,比如请求某个 URL 、发起一个 GET 请求、表单提交方式为 POST POST 请求、表单提交方式为 GET GET 请求。一次重定向则相当于前后发起了两次请求这些情况下有几次请求就会经过几次指定过滤器。
        属性 2.4 版本的 Servlet 中添加的新的属性标签总共有 4 个值,分别是 REQUEST FORWARD INCLUDE ERROR ,以下对这 4 个值进行简单说明;
1REQUEST

只要发起的操作是一次 HTTP 请求,比如请求某个 URL 、发起一个 GET 请求、表单提交方式为 POST POST 请求、表单提交方式为 GET GET 请求,就会经过指定的过滤器。
2FORWARD

只有当当前页面是通过请求转发过来的情形时,才会经过指定的过滤器。
3INCLUDE

只要是通过 嵌入的页面,每嵌入一个页面都会经过一次指定的过滤器。
4ERROR

假如 web.xml 中配置了 ,如下代码所示:
 
    400 
    /filter/error.jsp 
        意思是 HTTP 请求响应的状态码只要是 400 404 500 3 种状态码之一,容器就会将请求转发到 error.jsp 下,这就触发了一次 error ,经过配置的 DispatchFilter
        需要注意的是,虽然把请求转发到 error.jsp 是一次 forward 的过程,但是配置成
FORWARD 不会经过 DispatchFilter 过滤器。
        这4 dispatcher 方式可以单独使用,也可以组合使用,只需配置多个
即可
        审计时的过滤器 属性中使用的值也是我们关注的一个点。属性会指明我们要过滤访问哪些资源的请求,“/* 的意思是拦截所有对后台的请求, 包括一个简单的对 JSP 页面的 GET 请求。同时我们可以具体地指定拦截对某一资源的请求,同时也可以设置对某些资源的请求不进行过滤而单独放过,示例代码如下:
 
    XSSEscape 
    com.springtest.filter.XssFilter 
 
 
    XSSEscape 
    /com/app/UserControl 
    REQUEST 

        既然能够指定单独过滤特定资源,自然也就可以指定放行特定资源。

        设置对全局资源请求过滤肯定是不合理的。生产环境中有很多静态资源不需要进行过滤,所以我们可以指定将这些资源进行放行,示例代码如下:
 
     XSSEscape  
     com.springtest.filter.XssFilter  
         

            excludedPaths 
            /pages/*,*.html,*.js,*.ico 
         
 
 
     XSSEscape  
    /* 
        这样配置后,如果有对 html js ico 资源发起的请求, Serlvet 在路径选择时就不会将该请求转发至 XssFilter 类。
        在审计代码时,这也是需要注意的一个点,因为开发人员的错误配置有可能导致本应该经过过滤器的请求却被直接放行,从而使项目中的过滤器失效。

        了解标签的作用后,查看 XssFilter 类的内容,如图 7-6 所示

图 7-6 filter 包的内容

可以看到 filter 包下有两个 Java 类,先来查看 XssFilter 类,如下代码所示

import javax.servlet.*;
import javax.servlethttp.HttpServletRequest; 
import java.io.Ioexception;


public class XssFilter implements Filter{
@Override
public void init(FilterConfig filterConfig) throws ServletException {

}
@Override
public void doFilter(ServletRequest reguest ServletResponse response FilterChain chain
throws I0Exception,ServletException {

chain.doFilter(new XSSRequestWrapper((HttpServletRequest)request),response);

}
@Override
public void destroy() {
    }
}
        可以看到 XssFilter 类实现了一个 Filter 接口,Filter 是 Servlet 的三大组件之一, javax.servlet.Filter 是一个接口,其主要作用是过滤请求,实现请求的拦截或者放行,并且添加新的功能。
         Filter 接口中有 3 个方法,这里进行简单介绍:
  1. init 方法:在创建完过滤器对象之后被调用。只执行一次。
  2. doFilter 方法:执行过滤任务方法。执行多次。
  3. destroy 方法:Web 服务器停止或者 Web 应用重新加载,销毁过滤器对象。
        当 Servlet 容器开始调用某个 Servlet 程序时,如果发现已经注册了一个 Filter程序来对该 Servlet 进行拦截,那么容器不再直接调用 Servlet service 方法,而是调用 Filter doFilter 方法, doFilter 方法决定是否激活 service 方法
         不难看出,需要我们重点关注的方法是 doFilter 方法,如下代码 所示:
@Override
public void doFilter(ServletRequest reguest ServletResponse response FilterChain chain
throws I0Exception,ServletException {

chain.doFilter(new XSSRequestWrapper((HttpServletRequest)request),response);

}
        这里的 request 参数和 response 参数可以理解为封装了请求数据和响应数据的对
象,需要过滤的数据存放在这两个对象中。对于最后一个参数 FilterChain ,通过名称可以猜测这个参数是一个过滤链。查看FilterChain 的源码,如下代码 所示:
public interface FilterChain {

/**
* Causes the next filter in the chain to be invoked or if the calling filter is the last filter* in the chain, causes the resource at the end of the chain to be invoked.

* @param request the request to pass along the chain.* @param response the response to pass along the chain.
¥
* @since 2.3*/

public void doFilter ( ServletRequest requestServletResponse response)throws I0Exception, ServletException;
}
        可以发现 FilterChain 是一个接口,而且该接口只有一个 doFilter 方法。 FilterChain参数存在的意义就在于,在一个 Web 应用程序中可以注册多个 Filter 程序,每个Filter 程序都可以对一个或一组 Servlet 程序进行拦截。如果有多个 Filter 程序,就可以对某个 Servlet 程序的访问过程进行拦截,当针对该 Servlet 的访问请求到达时,Web 容器将把多个 Filter 程序组合成一个 Filter (也叫作过滤器链)。
        Filter 链中的各个 Filter 的拦截顺序与它们在 web.xml 文件中的映射顺序一致,在上一个 Filter.doFilter 方法中调用 FilterChain.doFilter 方法将激活下一个Filter 的 doFilter 方法最后一个 Filter.doFilter 方法中调用的 FilterChain.doFilter 方法将激活目标 Servlet service 方法。
        只要 Filter 链中任意一个 Filter 没有调用 FilterChain.doFilter 方法,则目标
Servlet service 方法就都不会被执行。
        大家不难发现,虽然 FilterChain 名称看起来像过滤器,但是调用 chain.dofilter方法似乎并没有执行任何类似过滤的工作,也没有任何类似黑名单或者白名单的过滤规则。
        在调用 chain.dofilter 方法时,我们传递了两个参数:new XSSRequestWrapper
((HttpServletRequest) request) response ,就是说我们传递了一个 XSSRequestWrapper
对象和 ServletRespons 对象,我们关心的当然是这个 * XSSRequestWrapper 对象
        在传递参数的过程中,我们通过调用 XSSRequestWrapper 的构造器传递了
HttpServletRequest 对象,这里简单从继承关系理解  HttpServletRequest
ServletRequest 的关系,如图 7-7  所示
Java EE 开发框架安全审计——SSM框架(入门篇)_第13张图片 图 7-7 HttpServletRequest 和 ServletRequest 的关系
这里生成一个 XSSRequestWrapper 对象并传入了参数,如图 7-7  所示:

图 7-7 生成一个 XSSRequestWrapper 对象

 filter 下面有一个叫作 XSSRequestWrapper 的类,如下代码所示:

public class XSSRequestWrapper extends HttpServletRequestWrapper{
    
    public XSSRequestWrapper(HttpServletRequest request){
        super(request);
            }
        @Override
        public String getHeader(String name){
            return StringEscapeUtils.escapeHtml3(super.getHeader(name));
                }
        @Override
        public String getQueryString(){
            return StringEscapeUtils.escapeHtml3(super.getQueryString();
                }
        @Override
        public String getParameter(String name){
            return StringEscapUtils.escapeHtml3(super.getParamter(name));
                }
        @Override
        public String[] getParameterValues(String name){
            String[] values = super.getParameterValues(name);
            if(values!=null){
                int length = value.length;
                String[] escapeValues = new String[length];
                for(int i=0;i':
                    buffer.append(">");
                    break;
                case'&':
                    buffer.append('&');
                    break;
                case'"':
                    buffer.append(""");
                    break;
                case 10:
                case 13:
                    break;
                default:
                    buffer.append(c);\
                }
            }
            html = buffer.toString();
            return html;
    }
}
        可以发现过滤行为在这里进行,而 XssFilter 的存在只是在链式执行过滤器,并最终将值传给 Servlet 时调用 XSSRequestWrapper 来进行过滤并获取过滤结果。
        这里不再对过滤规则过多介绍,网上有很多好的过滤规则,黑名单和白名单......

        可能会有这的疑惑,为什么不将过滤的逻辑代码写在 XssFilter 中?看过我之前的文章的应该明白,这是为了解耦,其次是因为 XSSRequestWrapper 继承了一个HttpServletRequestWrapper

         查看 HttpServletRequestWrapper 类的继承关系,如图 7-8  所示
Java EE 开发框架安全审计——SSM框架(入门篇)_第14张图片 图 7-8 HttpServletRequestWrapper 类的继承关系

可以看到 HttpServletRequestWrapper 实现了 HttpServletRequest 接口。我们的想法是尽可能将请求中有危害的数据或者特殊符号过滤掉,然后将过滤后的数据转发向后面的业务代码并继续执行,而不是发现请求数据中有特殊字符就直接停止执行,抛出异常,返回给用户一个 400 页面。因此要修改或者转义 HttpServletRequest 对象中的恶意数据或者特殊字符。然而 HttpServletRequest 对象中的数据 不允许被修改,也就是说,HttpServletRequest 对象没有为用户提供直接修改请求数据的方法。
因此就需要用到 HttpServletRequestWrapper 类,这里用到了常见的 23 种中设计模式之一的装饰者模式,限于篇幅原因这里不对装饰者模式进行讲解(Buffer也是这种思想),感兴趣的读者可以自行研究。HttpServletRequestWrapper 类为用户提供了修改 request 请求数据的方法,这也是需要单写一个类来进行过滤的原因,是因为框架就是这么设计的。
        当 HttpServletRequestWrapper 过滤完请求中的数据并完成修改后,返回作为chain.doFilter 方法的形参进行传递。
        最后一个 Filter.doFilter 方法中调用的 FilterChain.doFilter 方法将激活目标Servlet 的 service 方法。
        由于我们没有配置第二个 Filter ,因此 XssFilter 中的 chain.doFilter 将会激活 Servlet  service 方法,即 DispatcherServlet service 方法,然后数据将传入 Spring MVC 的 Controller 层并交由 BookController 来处理。 现在使用 Filter 来演示效果。首先设置断点,如图 7-9  所示。
Java EE 开发框架安全审计——SSM框架(入门篇)_第15张图片 图 7-9 设置断点

        再次执行到这里时,XSS 语句中的特殊字符已经被 Filter 转义,如图 7-10  和图 7-11 所示,自然也不会存在 XSS 的问题了。

Java EE 开发框架安全审计——SSM框架(入门篇)_第16张图片 图 7-10 XSS 语句中的特殊字符被 Filter 转义

Java EE 开发框架安全审计——SSM框架(入门篇)_第17张图片 图 7-11 XSS 语句被转移

你可能感兴趣的:(Javaee,java-ee,mybatis,安全,网络安全,spring)