作者:天融信阿爾法實驗室
公眾號:Java框架級SSM代碼審計思路
1 SSM框架簡介
SSM框架,即SpringMVC+Spring+Mybatis三個開源框架整合在一起的縮寫。
在SSM框架之前生產環境中SSH框架占據多數,即Struts2+Spring+Hibernate三個開源框架整合而成。后因Struts2爆出眾多高危漏洞,導致目前SSM逐漸代替SSH成為主流開發框架的選擇。
審計SSM框架首先就要對MVC設計模式和,web三層架構有一定程度的了解,限于篇幅原因這里就簡單介紹一下
1.1 SpringMVC
是一種基于Java的實現MVC設計模式的請求驅動類型的輕量級Web框架,使用了MVC架構模式的思想,將web層進行職責解耦,基于請求驅動指的就是使用請求-響應模型,框架的目的就是幫助我們簡化開發。
1.2 Spring
是分層的 Java SE/EE full-stack 輕量級開源框架,以 IOC(Inverse of Control,控制反轉)和 AOP(Aspect Oriented Programming,面向切面編程)為內核,使用基本的 JavaBean 完成以前只可能由 EJB 完成的工作,取代了 EJB 臃腫和低效的開發模式,Spring的用途不僅僅限于服務器端的開發。從簡單性、可測試性和松耦合性角度而言,絕大部分Java應用都可以從Spring中受益
1.3 Mybatis
是支持定制化 SQL、存儲過程以及高級映射的優秀的持久層框架。MyBatis 避免了幾乎所有的 JDBC 代碼和手動設置參數以及獲取結果集。MyBatis 可以對配置和原生Map使用簡單的 XML 或注解,將接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java對象)映射成數據庫中的記錄。
1.4 Servlet
還有一項技術雖然名稱沒有出現在這三個開源框架中但是SpringMVC的底層就是以此技術進行構建的,這項技術就是Servlet
Servlet是基于Java技術的Web組件,由容器管理并產生動態的內容。Servlet與客戶端通過Servlet容器實現的請求/響應模型進行交互。
相對以SSM框架搭建的java web項目進行審計,上述這些都是要有一定程度的了解的。
2 SSM框架代碼執行流程和審計思路
2.1 審計的出發點web.xml
其實代碼審計的核心思想就是追蹤參數,而追蹤參數的步驟就是程序執行的步驟,說白了代碼審計就是一個跟蹤程序執行步驟的一個過程,當我們知道了SSM框架的一個執行流程,自然就知道了如何如跟蹤一個參數,剩下的就是去觀察在參數傳遞的過程中有沒有一些常見的漏洞點。
我們這里通過一個簡單的Demo來描述一下SSM框架搭建的項目是如何完成一次用戶請求,它的流程是怎么樣的,而參數又是怎樣被傳遞怎樣被過濾的,當我們明白了這些,就可以嘗試自己上手一些SSM的項目審計。
首先我把Demo的全部文件和文件結構粘貼出來

這是一個簡單的圖書管理Demo目錄,功能是對圖書名稱,數量和簡介簡單的增刪改查
首先不管我們是審計一個項目還是包括Tomcat加載一個項目一般都是由web.xml這個文件開始的
當然一個項目中沒有web.xml也是可以的,可以通過servlet3.0開始提供的一些新注解來達到和配置web.xml一樣的效果,但是這樣的項目很少會碰到,所以我們以主流的配置web.xml的項目來作為講解。
Tomcat會加載web.xml文件讀取文件中的內容

web.xml文件主要的工作包括兩部分:1、web.xml啟動spring容器;2、DispathcheServlet的聲明;3、其余工作是session過期,字符串編碼等
首先是生成DispatcherServlet類,DispatcherServlet是前端控制器設計模式的實現,提供Spring Web MVC的集中訪問點(也就是把前端請求分發到目標controller),而且與Spring IoC容器無縫集成,從而可以獲得Spring的所有好處。
簡單理解就是將我們的請求轉發至我們的SpringMVC中去,交由我們SpringMVC的Controller來進行接下來的處理
然后下面有一個
下面的
下面的
2.2 Spring核心配置文件applicationContext.xml
然后我們根據加載順序去看applicationContext.xml

applicationContext.xml中包含了三個配置文件,這三個配置文件就是我們用Spring來整合SpringMVC和Mybaits的配置文件,其實這三個配置文件中的內容都可以直接寫applicationContext.xml中因為applicationContext.xml是Spring的核心配置文件,例如生成Bean,配置連接池,生成sqlSessionFactory。但是為了便于理解,我們這些配置分別寫在三個配置文件中,由applicationContext.xml將這三個xml進行關聯,由下面這張截圖我們可以清晰的看到applicationContext.xml將這三個配置文件關聯了起來。

首先數據經由DispatcherServlet派發至Spring-mvc的controller層所以我們先看Spring-mvc.xml這個配置文件

如果在web.xml中servlet-mapping的url-pattern設置的是/,而不是如.do。表示將所有的文件,包含靜態資源文件都交給spring mvc處理。就需要用到
在Spring-mvc.xml中配置
剩下兩項一個是指定了返回的view所在的路徑,另一個是指定SpringMVC注解的掃描路徑
不難看出這個配置文件中都是和Spring-mvc相關的配置。
2.3 SSM之SpringMVC執行流程
接下來就是我們SpringMVC controller層接受前臺傳入的數據,這里我們讓demo跑起來以方便演示和講解

這是我們的index首頁,我們看下頁面源碼

可以看到a標簽的超鏈接是http://localhost:8080/SSMFrameWorkTest_war/book/allbook
${pageContext.request.contextPath}
是JSP取得絕對路徑的方法, 也就是取出部署的應用程序名或者是當前的項目名稱,這樣當我們把項目部署到生產環境中時不容易出錯
后臺此時收到的請求路徑為/book/allbook首先SpringMVC在項目啟動時會去掃描我們指定的要掃描的路徑也就是com.kuang.controller這個路徑下的所有類,我們看下BookController這個類的代碼

SpringMVC會掃描這個類中的所有注解,當看到@Controller是會生成該controller的Bean,掃描到@RequestMappting注解的時候會將@RequestMappting中的URI和下面的方法形成映射。所以我們請求的URI是“/book/allBool” SpringMVC就會將數據交由BookController類的list方法來處理
我們仔細觀察list方法,里面調用了bookService參數的queryAllBook方法,這里使用到了兩個注解@Autowired,@Qualifier,簡單介紹下兩個注解的作用
@Autowrite
作用:自動按照類型注入,只要有唯一的類型匹配就能注入成功,當傳入的類型不唯一時,則會報錯。
@Qualiier
作用:在自動按照類型注入的基礎上,在按照bean的id注入。它在給類成員注入數據時,不能獨立使用。但是再給方法的形參注入數據的時候,可以獨立使用。
由此可以看到bookService參數的類型是BookService類型,通過注解自動注入的Bean的id叫做BookServiceImpl
2.4 SSM之Spring執行流程
這里我們就要從SpringMVC的部分過渡到Spring的部分了,所謂的過渡就是我們從SpringMVC的Controller層去調用Service層而這Service就是我們使用Spring進行IOC控制和AOP編程的地方。
首先我們需要先要去看spring-service.xml這個配置文件,

這里我們看到了一個很重要的東西 id為BookServiceImpl的bean,我們可以看到這個bean的class路徑是com.kuang.service.BookServiceImpl,
接下來我們去看BookServiceImpl這個類的詳細信息

首先看到該類是實現了BookService這個接口,ok我們先去看BookService這個接口

可以看到接口中定義了四種方法,為了方便理解,這些方法的名字是對應著日常項目中最長用的操作數據庫的四個方法即,增刪改查。
好了,看完了接口我們來看接口的實現類也就是BookServiceImpl。

由于實現了BookService這個接口,自然也就需要實現該接口下的所有方法,我們找到queryAllBook方法,發現queryAllBook調用了bookMapper參數的queryAllBook方法,而bookMapper是BookMapper類型的參數。
我們回過頭來看spring-service.xml中的這一項配置,之前說了這一配置是將BookServiceImpl這個類生成一個bean并放入Spring 的IOC容器中,
并且提供了該屬性的set方法, ref屬性是指要注入的value是其他的Bean類型,如果傳入的是一些基本類型或者String類型就不需要用ref 將ref改成value就可以

這里我們可以看到我們通過ref屬性向BookServiceImpl這個類中的bookMapper參數注入了一個value,這個value是一個其他的bean類型,這個bean的id叫做bookMapper。此時由于我們Service層的BookServiceImpl的queryAllBook方法的實現方式其實就是調用了id為bookMapper的bean的queryAllBook方法。所以這個id為bookMapper的bean就是程序執行的下一步。
2.5 SSM之Mybatis執行流程
接下來就是是我們的web三層架構的數據訪問層也就是我們Mybaits負責的部分,通常這一部分的包名會叫做xxxdao,也就是開發中常說的dao層,該包下面的類和接口通常都叫做xxxDao或者xxxMapper,當然不遵守這個規范也可以但是不推薦。此時我們的請求要從Spring負責的業務層過渡到Mybatis負責的數據層了,但是Mybaits和Spring的關系不像SpringMVC和Spring的關系一樣可以無縫銜接,所以我們需要通過配置文件將Mybatis和Spring關聯起來,這里我們看一下pom.xml

可以看到我們導入的包除了Mybatis本身,還倒入了一個mybatis-spring的包,目的就是為了將Mybatis和Spring做結合,spring-dao.xml也就是用來整合Spring和Mybatis的配置文件。
剛才我們看到Spring啟動加載bean時會注入一個id為bookMapper的bean但是我們并未在之前的任何配置文件包括注解中看到有這個bean的相關信息,所以我們接下來要看spring-dao.xml中有沒有和這個bean有關的信息

每項配置的作用基本都用注釋的方式標明了
這里關聯了一個properties文件

里面是連接數據庫和配置連接池時需要的信息,沒什么好說的。
我們著重看這個配置

這個配置通過生成MapperScannerConfigurer的bean來實現自動掃描com.kuang.dao下面的接口包,然后動態注入到Spring IOC容器中,同樣動態注入的bean的id默認為類名(開頭字母小寫),我們看下到目錄下有哪些文件。

我們看到有一個叫BookMapper的接口文件,這樣就明白了之前生成BookServiceImpl這個bean是通過
public void setBookMapper(BookMapper bookMapper) {
? this.bookMapper = bookMapper;
}
方法注入的這個bookMapper是哪里來的了,是由我們配置了MapperScannerConfigurer這個bean后這個bean幫我們掃描dao包下的借口文件并生成bean然后再幫我們注入到Spring的IOC容器中,所以我們才可以在BookServiceImpl這個bean中通過
然后我們來看這項配置

這里是生成一個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語句。
SqlSessionFactory和SqlSession的實現過程:
mybatis框架主要是圍繞著SqlSessionFactory進行的,創建過程大概如下:
(1)、定義一個Configuration對象,其中包含數據源、事務、mapper文件資源以及影響數據庫行為屬性設置settings
(2)、通過配置對象,則可以創建一個SqlSessionFactoryBuilder對象
(3)、通過 SqlSessionFactoryBuilder 獲得SqlSessionFactory 的實例。
(4)、SqlSessionFactory 的實例可以獲得操作數據的SqlSession實例,通過這個實例對數據庫進行
如果是spring和mybaits整合之后的配置文件,一般以這種方式實現,SqlSessionFactory的創建:
SqlSessionFactoryBean是一個工廠Bean,根據配置來創建SqlSessionFactory
如果是單獨的使用手動創建SqlSessionFactory和SqlSession話流程如下

看完了SqlSessionFactory和SqlSession的基礎知識我們同時注意到下面這個
這里又引入了一個xml配置文件,還記得我上面剛說過spring-dao.xml是用來整合Spring和Mybatis的么?這個mybatis-config.xml就是我們Mybatis的配置文件。
好了spring-dao.xml這個用來整合Spring和Mybatis的配置文件我們已經了解了,程序按著剛才的請求接著向下走。
我們剛在走到了BookServiceImpl類的queryAllBook方法,然后該方法又是調用了bookMapper的queryAllBook方法。現在我們清楚了bookMapper的類型是BookMapper
又從sping-dao.xml的配置文件中看到了該文件的位置位于com.kuang.dao路徑下,我們現在就打開BookMapper.java文件看一看

我們注意到這只是個接口,我們都知道接口是不能實例化的接口只是提供一個規范,這是我們就有疑問了,那我們調用的bookMapper的queryAllBook是怎么執行的?
我們在仔細看下dao目錄下的文件,

發現還有一個名字和BookMapper.java名字一樣的xml文件,我們打開看一下內容。

看到這個文件,雖然我們對mybatis的了解不多,但是我們應該大概明白了,為什么我們BookMapper明明只是接口,我們卻可以實例化生成BookMapper的bean并且可以調用他的方法了。
但是只有BookMapper.java和BookMapper.xml顯然不能就是Mybatis的全部了,兩個文件之間此時除了名字相同以外還沒有什么直接聯系,所以我們還需要關聯起來,我們來看看mybatis-config.xml這個Mybatis的配置文件

我們看到了
也就是說最終由我們的Spring幫我生成BookMapper的代理對象然后由Mybaits通過
我們看到我們此次請求最終調用的BookMapper的queryAllBook方法,這時我們就需要去BookMapper.xml去尋找與之對應的Sql語句了

很容易就找到了
我們看到最后執行的sql語句是
SELECT * from ssmbuild.books
至此我們的請求已經完成從一開始的由DispatcherServlet這個前端控制器派發給SpringMVC并最終通過Mybatis 執行我們需要對數據庫進行的操作。
生產環境的業務代碼,會比這個Demo復雜,但是整體的執行流程和思路并不會有什么太大的變化,所以審計思路也是如此。
SSM框架有三種配置方式,即全局采用xml配置文件的形式,全局采取注解的配置方式,或者注解和xml配置文件配合使用的方式,區別只是在于寫法不一樣,執行流程不會因此發生太多改變。
2.6 審計的重點filter過濾器
此時在將web.xml時還有一個標簽說放在后面講,就是web.xml的
SpringMVC時構建于Servlet之上的,所以Servlet中的過濾器自然也是可以使用,只不過不能配置在spring-mvc.xml中,而是要直接配置在web.xml中,因為是屬于Servlet的技術嘛。
我們重回web.xml

為了方便之前的講解,我將這兩個filter注釋掉了。也就是說這兩個filter并沒有生效。我們以下面的filter-name為XSSEscape的filter來進行講解。
首先我們此時程序是沒有XSS防護的,所以存在存儲型XSS漏洞,我們來嘗試存儲型XSS攻擊

我們點擊新增功能

看一下提交路徑

去后臺找與之對應的方法

找到后在這里下斷點看傳入參數的詳細信息

看到沒有任何過濾XSS語句就這么直接傳了進來

如果我們此時想要防御這個XSS攻擊就可以在web.xml中配置上我們的

這里聲明了了我在com.kuang.filter的包路徑下又一個類叫XssFilter是一個過濾器
下面的
只要發起的操作是一次HTTP請求,比如請求某個URL、發起了一個GET請求、表單提交方式為POST的POST請求、表單提交方式為GET的GET請求。一次重定向則前后相當于發起了兩次請求,這些情況下有幾次請求就會走幾次指定過濾器。
1、REQUEST
只要發起的操作是一次HTTP請求,比如請求某個URL、發起了一個GET請求、表單提交方式為POST的POST請求、表單提交方式為GET的GET請求。一次重定向則前后相當于發起了兩次請求,這些情況下有幾次請求就會走幾次指定過濾器。
2、FOWARD
只有當當前頁面是通過請求轉發轉發過來的情形時,才會走指定的過濾器
3、INCLUDE
只要是通過
4、ERROR
假如web.xml里面配置了<error-page></error-page>:
例如
<error-page>
<error-code>400</error-code>
<location>/filter/error.jsp</location>
</error-page>
意思是HTTP請求響應的狀態碼只要是400、404、500三種狀態碼之一,容器就會將請求轉發到error.jsp下,這就觸發了一次error,走進了配置的DispatchFilter。需要注意的是注意一點的是,雖然把請求轉發到error.jsp是一次forward的過程,但是配置成
這四種dispatcher方式可以單獨使用,也可以組合使用,配置多個<dispatcher></dispatcher>即可。
在審計的時候的過濾器<dispatcher>屬性中使用的值也是我們關注的一個點,
<url-pattern>屬性是指明我們要過濾訪問哪些資源的請求,“/*”的意思就是攔截所有對后臺的請求, 包括對一個簡單的對jsp頁面的GET請求,同時我們可以具體的指定攔截對某一資源的請求,同時也可以設置對某些資源的請求不過濾單獨進行放過,
舉例說明
<filter>
<filter-name>XSSEscape</filter-name>
<filter-class>com.springtest.filter.XssFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>XSSEscape</filter-name>
<url-pattern>/com/app/UserControl</url-pattern>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
既然等指定單獨過濾特定資源,自然也就可以指定對特定資源的放行。
如果設置全局的資源請求過濾的話肯定是不合理的,生產環境中又很多靜態資源是不需要進行過濾的,所以我們可以指定將這些資源進行放行,
例如
<filter>
<filter-name> XSSEscape </filter-name>
<filter-class> com.springtest.filter.XssFilter </filter-class>
<init-param>
<!-- 配置不需要被登錄過濾器攔截的鏈接,只支持配后綴、前綴 及全路徑,多個配置用逗號分隔 -->
<param-name>excludedPaths</param-name>
<param-value>/pages/*,*.html,*.js,*.ico</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name> XSSEscape </filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
這樣我們的serlvet在路徑選擇時當有對 html js 和ico資源發起的請求就不回在將改請求轉發至XssFilter類。
我們在審計代碼時 這里也是需要注意的一個點,因為有可能開發人員的錯誤配置導致本應該經過過濾器的請求,錯誤的給直接放行了,這樣即使項目中有過濾器,也是不會生效的。
明白了<filter>標簽的作用我們就去看XssFilter這個類的內容

可以看到filter包下有兩個java類,我們先看XssFilter這個類

可以看到我們的XssFilter這個類實現了一個叫Filter的接口
我們去看一下Filter接口的源碼

可以看到Filter所屬的包是javax.servlet
Filter是Servlet的三大組件之一
javax.servlet.Filter 是一個接口,過濾請求,實現請求的攔截或者放行,并且添加新的功能
眾所周知接口其實就是一個標準,所以我們想要編寫自己的過濾器自然也要遵守這個標準即實現Filter這個接口。
Filter接口中有三個方法,這里簡單介紹一下
init方法:
在創建完過濾器對象之后被調用。只執行一次
doFilter方法:
執行過濾任務方法。執行多次。
destroy方法:
Web服務器停止或者Web應用重新加載,銷毀過濾器對象。
當 Servlet 容器開始調用某個 Servlet 程序時,如果發現已經注冊了一個 Filter 程序來對該 Servlet 進行攔截,那么容器不再直接調用 Servlet 的 service 方法,而是調用 Filter 的 doFilter 方法,再由 doFilter 方法決定是否去激活 service 方法
不難看出需要我們重點關注的方法是doFilter方法

這里的request的參數和response參數可以理解成封裝了請求數據和相應數據的對象,我們需要過濾的數據就是存放在這兩個對象中,
最后一個參數FilterChain,通過名字我們猜這個參數是一個過濾鏈,查看一下FilterChain的源碼

看到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的關系

既然這里生成了一個XSSRequestWrapper對象并傳入的參數那我們自然要跟進一探究竟

正好filter下面有一個叫XSSRequestWrapper的類,我們看一下代碼

看到這里大家應該恍然大悟,原來過濾的行為是在這里進行了,而XssFilter的存在只是在鏈式執行過濾器并最終將值傳給Servlet時調用XSSRequestWrapper來進行過濾并獲取過濾結果而已。
這里對過濾規則就不過多贅述,網上有很多好的過濾規則,這里就不多提了。
這里肯定有很多人并明白問什么不將過濾的邏輯代碼寫在XssFilter中而是又新寫了一個類,不是多此一舉么?
這么做當然不是多此一舉,首先解耦這個理由就已經足夠充分了,其次我們看到XSSRequestWrapper繼承了一個類 HttpServletRequestWrapper
這里我們看一下HttpServletRequestWrapper類的繼承關系

我們可以看到HttpServletRequestWrapper是實現了HttpServletRequest接口的,我們這里提一下過濾這個概念,我們的想法是盡可能的把請求中的有危害的數據或者特殊符號過濾掉,然后將過濾后的數據轉發向后面的業務代碼并繼續執行,而不是說發現請求數據中有特殊字符就直接停止執行,拋出異常,返回給用戶一個400頁面,所以既然要繼續執行,那我們就要去修改或者轉義HttpServletRequest對象中的惡意數據或者特殊字符。然而HttpServletRequest對象中的數據是不允許被修改的,也就是說HttpServletRequest對象沒有提供給我們直接修改請求數據的方法。
此時矛盾就來了,我們想要修改但是HttpServletRequest對象又不給提供,所以HttpServletRequestWrapper這個類就出現了,這里用到了常見的23中設計模式之一的裝飾者模式,限于篇幅原因不可能對裝飾者模式在進行講解了,感興趣的同學可以自己去研究。也就是說HttpServletRequestWrapper這個類的出現就是為了給我們提供修改request請求數據的方法的,到這里大家應該就明白了為什么需要單寫一個類來進行過濾的行為,不是我們想著么寫,而是框架就這么設計的,為的就是解耦。
此時當HttpServletRequestWrapper將請求中的數據過濾完,并修改完成后返回然后作為chain.doFilter方法的形參進行傳遞。
結合之前說的,最后一個 Filter.doFilter 方法中調用的 FilterChain.doFilter 方法將激活目標 Servlet的service 方法
由于我們沒有配置第二個Filter所以XssFilter中的chain.doFilter將會激活我們Servlet的service方法即DispatcherServlet的service方法,然后數據將傳入我們的SpringMVC的Controller層交由我們的BookController來處理。
我們這次使用filter來演示一下效果

老地方下斷

然后再次執行到這里時XSS語句中的特殊字符已經被Filter轉義。

自然也就不會存在Xss的問題了。
3 SSM框架審計思路總結
3.1 思路總結
最后總結一下SSM框架的審計思路,審計思路其實就是我們代碼的執行思路
和審計非SSM框架代碼的主要區別就是在于SSM框架的各種XML配置,和注解配置,需要我們根據XML中的配置和注解來查看代碼的執行路徑,SSM框架中常見的注解和注解中的屬性,以及常見的標簽和標簽的各個屬性。
審計漏洞的方式同正常的java代碼審計沒有區別,網上有很多非常優秀的java代碼審計文章,關于每個漏洞的審計方式寫的都非常全面,我們需要的就只是將其移植到SSM框架的審計中來,我們明白SSM的執行流程了,自然就明白了該怎么在SSM框架中跟蹤參數,例如剛剛講的XSS漏洞,我們根據XML中的配置和注解中的配置一路跟到了Mybatis的mapper.xml這個映射文件,找到了最中執行的
insert into ssmbuild.books(bookName,bookCounts,detail)
values (#{bookName}, #{bookCounts}, #{detail})
這個sql語句,發現我們傳入的books參數直到sql語句執行的前一刻都沒有經過任何的過濾處理,所以此處插入數據庫的參數自然是不可信的臟數據。
當我們再次查詢這條數據并返回到前端時就非常可能造成存儲型XSS攻擊
我們在審計這類漏洞時,最簡單的方法就是先去web.xml中去查看有沒有配置相關的過濾器,如果有哪我們就去看看過濾器的規則是否嚴格,如果沒有那就很有可能存在漏洞。
3.2 補充知識
最后還要提一個必要重要的Mybaits知識點就是Mybatis的預編譯,關于java的預編譯簡單介紹一下
非預編譯的情況下我們每次執行sql都需要將slq和參數拼接在一起然后傳給數據庫編譯執行,這樣采用拼接的方式非常容易產生SQL注入漏洞,當然可以使用filter對參數進行過濾來避免產生SQL注入,
而在預編譯的情況下,程序會提前將我們的sql語句編譯好,程序執行的時候只需要將傳遞進來的參數交由數據庫進行操作就可以了,此時不論傳來的參數是什么,都不會被當成時SQL語句的一部分,因為真正的SQL語句已經提前編譯好了,所以即使不過濾也不會產生SQL注入這類漏洞,
以下面這個mapper.xml中的SQL語句舉例
insert into ssmbuild.books(bookName,bookCounts,detail)
values (#{bookName}, #{bookCounts}, #{detail})
#{bookName}這種形式的就是采用了預編譯的形式傳參,而以下這種形式
insert into ssmbuild.books(bookName,bookCounts,detail)
values ('${bookName}','${bookCounts}', '${detail}')
'${bookName}'這種寫法就是沒有使用預編譯的形式進行傳參數,此時如果不對傳入的參數進行過濾和校驗的話就會產生SQL注入漏洞
'${xxxx}'和#{xxxx}其實就是jdbc的Statement和PreparedStatement對象。
3.3 學習建議
整篇文章對SSM框架的整個執行流程和審計流程進行了簡單的講解,后續想要增強SSM框架的審計水平,推薦大家自己上手一些簡單SSM框架搭建的項目,實戰永遠是最快的學習方式,大家在審計SSM框架可能遇到的最大的困難就是有很多新的之前沒有碰到過的注解,和XML中一些SSM獨有的標簽,這些注解和標簽數量很多,沒有辦法在一篇文章中講完,大家碰到不懂的注解和標簽都可以通過官方提供的文檔和搜索引擎來尋找答案。
最后感謝大家的耐心觀看。
本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/1075/
暫無評論