七、在攔截器中進行XSS與SQL注入攔截

  • 2019 年 11 月 7 日
  • 筆記

本次開發環境為:
系統:Windows 10 10.0
JDK:JRE: 1.8.0_152-release-1136-b43 amd64 JVM: OpenJDK 64-Bit Server VM by JetBrains s.r.o
開發工具:IntelliJ IDEA 2018.1.8
springboot框架:2.2.0

直接上乾貨,不多廢話,相關問題歡迎在評論區指教。

1、首先準備本次會用到的相關jar包,在pom.xml中導入

        <!-- xss過濾組件 -->          <dependency>              <groupId>org.jsoup</groupId>              <artifactId>jsoup</artifactId>              <version>1.12.1</version>          </dependency>          <!-- StringUtil工具類-->          <dependency>              <groupId>org.apache.commons</groupId>              <artifactId>commons-lang3</artifactId>              <version>3.9</version>          </dependency>

2、開始製作一個字符串過濾工具,使其所有字符串都能按照相同規則進行過濾

import org.apache.commons.lang3.StringUtils;  import org.jsoup.Jsoup;  import org.jsoup.nodes.Document;  import org.jsoup.safety.Whitelist;    /**   * 類 {@code FilterCoreUtil} 用於Xss非法標籤過濾工具類 <br> 過濾html中的xss字符.   *   * 本軟件僅對本次教程負責,版權所有 <a href="http://www.cnhuashao.com">中國,華少</a><br>   *   * @author cnHuaShao   * <a href="mailto:[email protected]   * <p>   * ">cnHuaShao</a>   * 修改備註:   * @version v1.0.1 2019/11/5 19:42   */  public class FilterCoreUtil {        /**       * 使用自帶的basicWithImages 白名單       * 允許的便簽有a,b,blockquote,br,cite,code,dd,dl,dt,em,i,li,ol,p,pre,q,small,span,       * strike,strong,sub,sup,u,ul,img       * 以及a標籤的href,img標籤的src,align,alt,height,width,title屬性       */      private static final Whitelist WHITE_LIST = Whitelist.basicWithImages();      /** 配置過濾化參數,不對代碼進行格式化 */      private static final Document.OutputSettings OUTPUT_SETTINGS = new Document.OutputSettings().prettyPrint(false);      static {          // 富文本編輯時一些樣式是使用style來進行實現的          // 比如紅色字體 style="color:red;"          // 所以需要給所有標籤添加style屬性          WHITE_LIST.addAttributes(":all", "style");      }        /**       * 過濾主方法入口       * @param content 需要過濾的字符串       * @return 過濾後的字符串       */      public static String clean(String content) {          if(StringUtils.isNotBlank(content)){              content = content.trim();          }          return Jsoup.clean(content, "", WHITE_LIST, OUTPUT_SETTINGS);      }  }

3、工具準備完畢,開始進行搭建一個倉庫,用於處理所有的請求相關字符串

import com.cnhuashao.rapiddevelopment.core.demo4.util.FilterCoreUtil;  import org.apache.commons.lang3.StringUtils;    import javax.servlet.http.HttpServletRequest;  import javax.servlet.http.HttpServletRequestWrapper;    /**   * 類 {@code XssHttpServletRequestWrapper} Xss核心匹配類 <br> .   *   * 本軟件僅對本次教程負責,版權所有 <a href="http://www.cnhuashao.com">中國,華少</a><br>   *   * @author cnHuaShao   * <a href="mailto:[email protected]   * <p>   * ">cnHuaShao</a>   * 修改備註:   * @version v1.0.1 2019/11/5 19:30   */  public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {      /**       * 需要進行過濾的請求       */      HttpServletRequest orgRequest = null;      /**       * 是否啟用過濾       */      private boolean isIncludeRichText = false;        /**       * 深度過濾構造方法       * @param request 需要過濾的請求       * @param isIncludeRichText 是否進行過濾,默認false       */      public XssHttpServletRequestWrapper(HttpServletRequest request, boolean isIncludeRichText) {          super(request);          orgRequest = request;          this.isIncludeRichText = isIncludeRichText;      }        /**       * 覆蓋getParameter方法,將參數名和參數值都做xss過濾。<br/>       * 如果需要獲得原始的值,則通過super.getParameterValues(name)來獲取<br/>       * getParameterNames,getParameterValues和getParameterMap也可能需要覆蓋       */      @Override      public String getParameter(String name) {          Boolean flag = ("content".equals(name) || name.endsWith("WithHtml"));          if( flag && !isIncludeRichText){              return super.getParameter(name);          }          name = FilterCoreUtil.clean(name);          String value = super.getParameter(name);          if (StringUtils.isNotBlank(value)) {              value = FilterCoreUtil.clean(value);          }          return value;      }        @Override      public String[] getParameterValues(String name) {          String[] arr = super.getParameterValues(name);          if(arr != null){              for (int i=0;i<arr.length;i++) {                  arr[i] = FilterCoreUtil.clean(arr[i]);              }          }          return arr;      }          /**       * 覆蓋getHeader方法,將參數名和參數值都做xss過濾。<br/>       * 如果需要獲得原始的值,則通過super.getHeaders(name)來獲取<br/>       * getHeaderNames 也可能需要覆蓋       */      @Override      public String getHeader(String name) {          name = FilterCoreUtil.clean(name);          String value = super.getHeader(name);          if (StringUtils.isNotBlank(value)) {              value = FilterCoreUtil.clean(value);          }          return value;      }        /**       * 獲取最原始的request       *       * @return       */      public HttpServletRequest getOrgRequest() {          return orgRequest;      }        /**       * 獲取最原始的request的靜態方法       *       * @return       */      public static HttpServletRequest getOrgRequest(HttpServletRequest req) {          if (req instanceof XssHttpServletRequestWrapper) {              return ((XssHttpServletRequestWrapper) req).getOrgRequest();          }            return req;      }  }

4、Xss處理倉庫均準備妥當,下面就可以開始編寫統一的攔截器了

import org.apache.commons.lang3.BooleanUtils;  import org.apache.commons.lang3.StringUtils;  import org.slf4j.Logger;  import org.slf4j.LoggerFactory;    import javax.servlet.*;  import javax.servlet.http.HttpServletRequest;  import javax.servlet.http.HttpServletResponse;  import java.io.IOException;  import java.util.ArrayList;  import java.util.Date;  import java.util.List;  import java.util.regex.Matcher;  import java.util.regex.Pattern;    /**   * 類 {@code XssFilter} Xss防止注入攔截器 <br> 用於過濾web請求中關於xss相關攻擊的特定字符.   *   * 本軟件僅對本次教程負責,版權所有 <a href="http://www.cnhuashao.com">中國,華少</a><br>   *   * @author cnHuaShao   * <a href="mailto:[email protected]   * <p>   * ">cnHuaShao</a>   * 修改備註:   * @version v1.0.1 2019/11/5 19:10   */  public class XssFilter implements Filter {        private Logger log = LoggerFactory.getLogger(XssFilter.class);        /**       * 是否過濾富文本內容       */      private static boolean IS_INCLUDE_RICH_TEXT = false;        /**       * 預設定白名單地址       * 將根據該變量中設置的相關目錄進行直接放行操作。       */      public List<String> excludes = new ArrayList<>();        /**       * 攔截器核心處理單元       * 用於處理所有需要過濾的請求,在此進行確認其合法性       * @param request       * @param response       * @param filterChain       * @throws IOException       * @throws ServletException       */      @Override      public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException,ServletException {          if(log.isDebugEnabled()){              log.debug("-------------------- get into xss filter --------------------");          }          HttpServletRequest req = (HttpServletRequest) request;          HttpServletResponse resp = (HttpServletResponse) response;          //進行白名單過濾,如符合白名單,則直接放行          if(handleExcludeUrl(req, resp)){              filterChain.doFilter(request, response);              return;          }          //開始進行深度過濾,判定其攜帶參數是否合法          XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) request,IS_INCLUDE_RICH_TEXT);          filterChain.doFilter(xssRequest, response);      }        /**       *  白名單過濾器       * @param request 攔截的請求       * @param response 攔截的響應       * @return  是否符合白名單       */      private boolean handleExcludeUrl(HttpServletRequest request, HttpServletResponse response) {          //白名單為空時直接返回false,使其向下執行          if (excludes == null || excludes.isEmpty()) {              return false;          }          //提取訪問的URL地址          String url = request.getServletPath();          log.info("開始進行過濾{} {}",new Date(),url);          //開始根據白名單地址進行判定,如符合則直接放行          for (String pattern : excludes) {              Pattern p = Pattern.compile("^" + pattern);              Matcher m = p.matcher(url);              if (m.find()) {                  return true;              }          }            return false;      }        /**       * 初始化攔截器配置       * @param filterConfig       * @throws ServletException       */      @Override      public void init(FilterConfig filterConfig) throws ServletException {          if(log.isDebugEnabled()){              log.debug("----------------- xss filter init -----------------");          }          //獲取其初始化時預設置的深度過濾開關,根據其預設的true、false進行確定其是否開啟深度攔截          String isIncludeRichText = filterConfig.getInitParameter("isIncludeRichText");          if(StringUtils.isNotBlank(isIncludeRichText)){              IS_INCLUDE_RICH_TEXT = BooleanUtils.toBoolean(isIncludeRichText);          }          //獲取其初始化時預設置的白名單字符串,根據【,】符號進行截取存儲。          String temp = filterConfig.getInitParameter("excludes");          if (temp != null) {              String[] url = temp.split(",");              for (int i = 0; url != null && i < url.length; i++) {                  excludes.add(url[i]);              }          }      }  }

5、添加cookie攔截器

import org.slf4j.Logger;  import org.slf4j.LoggerFactory;    import javax.servlet.*;  import javax.servlet.http.Cookie;  import javax.servlet.http.HttpServletRequest;  import javax.servlet.http.HttpServletResponse;  import java.io.IOException;  import java.util.Date;    /**   * 類 {@code CookieFilter} 會話Cookie攔截 <br> 用於對所有web會話的Cookie進行安全檢查.   *   * 本軟件僅對本次教程負責,版權所有 <a href="http://www.cnhuashao.com">中國,華少</a><br>   *   * @author cnHuaShao   * <a href="mailto:[email protected]   * <p>   * ">cnHuaShao</a>   * 修改備註:   * @version v1.0.1 2019/11/5 20:10   */  public class CookieFilter implements Filter{      private static final Logger log = LoggerFactory.getLogger(CookieFilter.class);        /**       * 繼承方法,攔截器初始化邏輯       * @param filterConfig       * @throws ServletException       */      @Override      public void init(FilterConfig filterConfig) throws ServletException {        }        /**       * 自定義攔截器,用於Cookie全局設置       * @param request 請求       * @param response 響應       * @param chain  filterchain是servlet容器提供給開發人員的對象       * @throws IOException       * @throws ServletException       */      @Override      public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {          HttpServletRequest req = (HttpServletRequest) request;          HttpServletResponse resp = (HttpServletResponse) response;            //日誌打印          if(log.isDebugEnabled()){              String url = req.getServletPath();              log.debug("-------- get into Cookie filter {} {}",new Date(),url);          }          //獲取請求中的cookies          Cookie[] cookies = req.getCookies();          //如果不為空,則開始對其中的所有cookie進行設置          if (cookies!=null){              for (Cookie cookie : cookies){                  if (cookie!=null){                      //設置cookie最大有效期,單位秒,當前設置一小時60*60                      cookie.setMaxAge(3600);                      //向瀏覽器指定,只允許https協議下才可以發送cookie                      cookie.setSecure(true);                      //設置cookie只能使用                      cookie.setHttpOnly(true);                      resp.addCookie(cookie);                  }              }          }          //請求下發          chain.doFilter(req,resp);      }        /**       * 繼承方法,在銷毀filter時進行的操作       */      @Override      public void destroy() {        }  }

6、所有攔截器準備就緒,在其SpringBoot啟動時進行加載

import com.cnhuashao.rapiddevelopment.core.demo4.filter.CookieFilter;  import com.cnhuashao.rapiddevelopment.core.demo4.filter.XssFilter;  import org.slf4j.Logger;  import org.slf4j.LoggerFactory;  import org.springframework.boot.web.servlet.FilterRegistrationBean;  import org.springframework.context.annotation.Bean;  import org.springframework.context.annotation.Configuration;    import java.util.HashMap;  import java.util.Map;    /**   * 類 {@code XssConfig} Xss配置加載類 <br> 用於將Xss攔截器在系統初始時加載至web服務器中.   * 本軟件僅對本次教程負責,版權所有 <a href="http://www.cnhuashao.com">中國,華少</a><br>   *   * @author cnHuaShao   * <a href="mailto:[email protected]   * <p>   * ">cnHuaShao</a>   * 修改備註:   * @version v1.0.1 2019/11/5 20:12   */  @Configuration  public class XssConfig {        private Logger log = LoggerFactory.getLogger(XssConfig.class);        /**       * cookie攔截器       * 用於Cookie全局設置,主要設置有效期、https安全訪問、httpOnly啟用       * @return       */      @Bean      public FilterRegistrationBean cookieFilterRegistrationBean(){          log.info("------------ Start Cookie Filter ------------");          //1、啟動攔截器          FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();          //註冊Cookie攔截器          filterRegistrationBean.setFilter(new CookieFilter());          //設置bean加載順序          filterRegistrationBean.setOrder(1);          //啟用註冊          filterRegistrationBean.setEnabled(true);          //添加URL為全部,使其攔截器全局攔截          filterRegistrationBean.addUrlPatterns("/*");          return filterRegistrationBean;      }        /**       * 配置初始全局攔截器Xss過濾器       * @return FilterRegistrationBean       */      @Bean      public FilterRegistrationBean xssFilterRegistrationBean() {          log.info("------------ Start Xss Filter ------------");          //1、啟動攔截器          FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();          //註冊Xss攔截器          filterRegistrationBean.setFilter(new XssFilter());          //設置bean加載順序          filterRegistrationBean.setOrder(1);          //啟用註冊          filterRegistrationBean.setEnabled(true);          //添加URL為全部,使其攔截器全局攔截          filterRegistrationBean.addUrlPatterns("/*");          //2、設置初始化方法          Map<String, String> initParameters = new HashMap<String,String>(2);          //設置白名單          initParameters.put("excludes", "/static/*,/img/*,/js/*,/css/*");          //是否啟用深度過濾機制(文本過濾機制),默認fales          initParameters.put("isIncludeRichText", "true");          //為此註冊設置init參數。調用此方法將替換任何*現有的init參數。          filterRegistrationBean.setInitParameters(initParameters);          return filterRegistrationBean;      }    }

7、進行測試攔截器效果

繼續訪問上一篇的地址:http://127.0.0.1:8081/hello?name=cnHuaShao

image.png

image.png

攔截器已成功過濾,至此我們所有的web請求均會經過該攔截器進行過濾,日常調配時只需要在攔截器類中進行配置即可

代碼示例

本文的相關例子可以查看倉庫中的RapidDevelopment-demo3目錄: Gitee 地址

本文聲明:

88×31.png

知識共享許可協議

本作品由 cn華少 採用 知識共享署名-非商業性使用 4.0 國際許可協議 進行許可。