Java中使用AntiSamy開源專案防禦XSS攻擊
背景:
之前公司有接了一個國土的專案,雖然是內部小專案,但是可能是zhengfu專案竟然找軟體測試公司大概測了一下。。。然後出現了以下三種問題:sql注入,XSS攻擊,介面訪問頻率。下面是解決XSS攻擊。
研究:
一開始的想法是,弄個過濾器把那些關鍵字過濾掉不就好了,例如script、eval、document等等,確實網上也有這些例子。可參考此部落格https://www.cnblogs.com/myyBlog/p/8890365.html
但是人家可是請專業的軟體測試工程師測試的,我這麼混過去肯定是不行滴,上面的主要只是簡單地過濾掉使用者的輸入引數,而且過濾得非常的不全面,可能還有很多方面的攻擊呢?然後繼續的百度找找有沒有此方面的開源專案。結果真的找到了一個,就是AntiSamy,OWASP的一個開源專案,通過對使用者輸入的 HTML / CSS / JavaScript 等內容進行檢驗和清理,確保輸入符合應用規範。AntiSamy被廣泛應用於Web服務對儲存型和反射型XSS的防禦中。這個就相當的強大了啊。
方案:
參考上面的部落格的做法:弄一個過濾器XSSFilter,攔截所有路徑。再使用HttpServletRequestWrapper來裝飾一下HttpServletRequest,已達到XSS清洗來防禦XSS攻擊。嗯,就這麼大概了,網友真強大~
開工:
1、引入Maven依賴:
<!-- OWASP AntiSamy 防止XSS攻擊--> <dependency> <groupId>org.owasp.antisamy</groupId> <artifactId>antisamy</artifactId> <version>1.5.5</version> </dependency>
2、策略檔案:
Maven下載下來的依賴裡面是帶有策略檔案,之前試過前一點的版本例如1.5.3好像是沒有的。下面我使用的策略檔案將是antisamy-ebay.xml,畢竟eBay 是當下最流行的線上拍賣網站之一,防禦策略肯定是非常好的。關於AntiSamy的策略檔案的詳解大家可以去搜一下,畢竟可能以後要改裡面的處理規則呢。
3、怎麼使用AntiSamy:
想當的簡單,首先要指定策略檔案生成Policy,然後新建AntiSamy,再對需要進行XSS清洗的內容進行掃面即可。
String xsshtml = "hyf<script>alert(1)</script>"; Policy policy = Policy.getInstance("antiSamyPath"); AntiSamy antiSamy = new AntiSamy(); CleanRequests cr = antiSamy.scan(xsshtml,policy); xsshtml = cr.getCleanHTML(); //清洗完的
4、建立XssFilter實現Filter介面然後重新doFilter方法:
因為我們對使用者的每次請求都進行攔截清洗,清洗完才能真正地呼叫Controller的方法,所以我們用到的是過濾器而不是攔截器。因為Filter是一個典型的過濾鏈,可以用來對 HttpServletRequest 進行預處理,或者是對 HttpServlerResponse 進行後處理。
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
*
*@author howinfun
*@date 2018/10/23
*@company DM
*@version 1.0
*/
public class XssFilter implements Filter {
private FilterConfig filterConfig;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest)servletRequest;
/* filterChain.doFilter(new XssHttpServletRequestWrapper2(request),servletResponse);*/
filterChain.doFilter(new XssHttpServletRequestWrapper(request),servletResponse); //包裝request,在裡頭進行XSS清洗
}
@Override
public void destroy() {
this.filterConfig = null;
}
}
5、新建一個自定義 HttpServletRequest 包裝類 XssHttpServletRequestWrapper ,繼承 HttpServletRequestWrapper 類:
對getParameter( String param)、getParameterValues( String param)以及 getHeader( String param) 等方法進行重寫,在重寫裡頭都進行一遍全方位清洗。
import org.apache.commons.lang.StringEscapeUtils;
import org.jeecgframework.web.appconfig.util.StringUtils;
import org.owasp.validator.html.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.util.Iterator;
import java.util.Map;
/**
* @desc 基於AntiSamy的XSS防禦
* @author howinfun
* @date 2018/10/23
* @company DM
* @version 1.0
*/
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
//AntiSamy使用的策略檔案
private static Policy policy = null;
static {
//指定策略檔案,策略檔案記得放在classpath下
String antiSamyPath = XssHttpServletRequestWrapper.class.getClassLoader().getResource("antisamy-ebay.xml").getFile();
System.out.println("policy_filepath:"+antiSamyPath);
if(antiSamyPath.startsWith("file")){
antiSamyPath = antiSamyPath.substring(6);
}
try {
policy = Policy.getInstance(antiSamyPath);
} catch (PolicyException e) {
e.printStackTrace();
}
}
public XssHttpServletRequestWrapper(HttpServletRequest request) {
super(request);
}
/**
* @desc Header為空直接返回,不然進行XSS清洗
* @author howinfun
* @date 2018/10/24
*
*/
@Override
public String getHeader(String name) {
String value = super.getHeader(name);
if(StringUtils.isEmpty(value)){
return value;
}
else{
String newValue = cleanXSS(value);
return newValue;
}
}
/**
* @desc Parameter為空直接返回,不然進行XSS清洗
* @author howinfun
* @date 2018/10/24
*
*/
@Override
public String getParameter(String name) {
String value = super.getParameter(name);
if(StringUtils.isEmpty(value)){
return value;
}
else{
value = cleanXSS(value);
return value;
}
}
/**
* @desc 對使用者輸入的引數值進行XSS清洗
* @author howinfun
* @date 2018/10/24
*
*/
@Override
public String[] getParameterValues(String name) {
String[] values = super.getParameterValues(name);
if (values != null) {
int length = values.length;
String[] escapseValues = new String[length];
for (int i = 0; i < length; i++) {
escapseValues[i] = cleanXSS(values[i]);
}
return escapseValues;
}
return super.getParameterValues(name);
}
@SuppressWarnings("rawtypes")
public Map<String,String[]> getParameterMap(){
Map<String,String[]> request_map = super.getParameterMap();
Iterator iterator = request_map.entrySet().iterator();
System.out.println("request_map"+request_map.size());
while(iterator.hasNext()){
Map.Entry me = (Map.Entry)iterator.next();
//System.out.println(me.getKey()+":");
String[] values = (String[])me.getValue();
for(int i = 0 ; i < values.length ; i++){
System.out.println(values[i]);
values[i] = cleanXSS(values[i]);
}
}
return request_map;
}
/**
* @desc AntiSamy清洗資料
* @author howinfun
* @date 2018/10/24
*
*/
private String cleanXSS(String taintedHTML) {
try{
AntiSamy antiSamy = new AntiSamy();
CleanResults cr = antiSamy.scan(taintedHTML, policy);
taintedHTML = cr.getCleanHTML();
return taintedHTML ;
}catch( ScanException e) {
e.printStackTrace();
}catch( PolicyException e) {
e.printStackTrace();
}
return taintedHTML;
}
}
6、在web.xml對新建的Filter進行註冊。
<!-- 防止XSS攻擊 -->
<filter>
<filter-name>XSSFilter</filter-name>
<filter-class>org.jeecgframework.core.filter.XssFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>XSSFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
7、接下來進行測試:
簡單的測試是否能過濾掉script標籤及裡面的內容。這個上傳的gif不動的,我就不放了,結果肯定是可以過濾掉滴。
8、繼續測試:
我繼續測試其他介面,測試了一個之前自己做的通用API,發現傳回來的JSON資料出現了問題,原來的雙引號被轉換為"。還測試到 被轉成亂碼了。。。所以我們必須在清洗完後進行進一步處理,將這兩個問題給自己解決掉。
/**
* @desc AntiSamy清洗資料
* @author howinfun
* @date 2018/10/24
*
*/
private String cleanXSS(String taintedHTML) {
try{
AntiSamy antiSamy = new AntiSamy();
final CleanResults cr = antiSamy.scan(taintedHTML,policy);
//AntiSamy會把“ ”轉換成亂碼,把雙引號轉換成""" 先將 的亂碼替換為空,雙引號的亂碼替換為雙引號
String str = StringEscapeUtils.unescapeHtml(cr.getCleanHTML());
str = str.replaceAll(antiSamy.scan(" ",policy).getCleanHTML(),"");
str = str.replaceAll(antiSamy.scan("\"",policy).getCleanHTML(),"\"");
return str;
}catch( ScanException e) {
e.printStackTrace();
}catch( PolicyException e) {
e.printStackTrace();
}
return taintedHTML;
}
到此,全部結束~