1. 程式人生 > >Java中使用AntiSamy開源專案防禦XSS攻擊

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資料出現了問題,原來的雙引號被轉換為&quot。還測試到&nbsp;被轉成亂碼了。。。所以我們必須在清洗完後進行進一步處理,將這兩個問題給自己解決掉。

/**
     * @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會把“&nbsp;”轉換成亂碼,把雙引號轉換成"&quot;" 先將&nbsp;的亂碼替換為空,雙引號的亂碼替換為雙引號
            String str = StringEscapeUtils.unescapeHtml(cr.getCleanHTML());
            str = str.replaceAll(antiSamy.scan("&nbsp;",policy).getCleanHTML(),"");
            str = str.replaceAll(antiSamy.scan("\"",policy).getCleanHTML(),"\"");
            return str;

        }catch( ScanException e) {
            e.printStackTrace();
        }catch( PolicyException e) {
            e.printStackTrace();
        }
        return taintedHTML;
    }

到此,全部結束~