最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

springboot2.x使用Jsoup防XSS攻擊的實現(xiàn)

 更新時間:2020年04月13日 11:49:48   作者:擼小魚  
這篇文章主要介紹了springboot2.x使用Jsoup防XSS攻擊的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

后端應(yīng)用經(jīng)常接收各種信息參數(shù),例如評論,回復(fù)等文本內(nèi)容。除了一些場景下面,可以特定接受的富文本標(biāo)簽和屬性之外(如:b,ul,li,h1, h2, h3...),需要過濾掉危險的字符和標(biāo)簽,防止xss攻擊。

一、什么是XSS?

看完這個,應(yīng)該有一個大致的概念。

XSS攻擊常識及常見的XSS攻擊腳本匯總
XSS過濾速查表

二、準(zhǔn)則

  • 永遠不要相信用戶的輸入和請求的參數(shù)(包括文字、上傳等一切內(nèi)容)
  • 參考第1條

三、實現(xiàn)做法

結(jié)合具體業(yè)務(wù)場景,對相應(yīng)內(nèi)容進行過濾,這里使用Jsoup。

jsoup是一款Java的HTML解析器。Jsoup提供的Whitelist(白名單)對文本內(nèi)容進行過濾,過濾掉字符、屬性,但是又保留必要的富文本格式。
如,白名單中允許b標(biāo)簽存在(并且不允許b標(biāo)簽帶有其他屬性)那么在一段Html內(nèi)容,在過濾之后,會變成:

過濾前:

<b style="xxx" onclick="<script>alert(0);</script>">abc</>

過濾后: 

<b>abc</b>

Whitelist主要方法說明

方法 說明
addAttributes(String tag, String... attributes) 給標(biāo)簽添加屬性。Tag是屬性名,keys對應(yīng)的是一個個屬性值。例如:addAttributes("a", "href", "class")表示:給標(biāo)簽a添加href和class屬性,即允許標(biāo)簽a包含href和class屬性。如果想給每一個標(biāo)簽添加一組屬性,使用:all。例如:addAttributes(":all", "class").即給每個標(biāo)簽添加class屬性。
addEnforcedAttribute(String tag, String attribute, String value) 給標(biāo)簽添加強制性屬性,如果標(biāo)簽已經(jīng)存在了要添加的屬性,則覆蓋原有值。tag:標(biāo)簽;key:標(biāo)簽的鍵;value:標(biāo)簽的鍵對應(yīng)的值。例如:addEnforcedAttribute("a", "rel", "nofollow")表示
addProtocols(String tag, String key, String...protocols) 給URL屬性添加協(xié)議。例如:addProtocols("a", "href", "ftp", "http", "https")標(biāo)簽a的href鍵可以指向的協(xié)議有ftp、http、https
addTags(String... tags) 向Whitelist添加標(biāo)簽
basic() 允許的標(biāo)簽包括: a, b, blockquote, br, cite, code, dd, dl, dt, em, i, li, ol, p, pre, q, small, strike, strong, sub, sup, u, ul,以及合適的屬性。標(biāo)簽a指向的連接可以是 http, https, ftp, mailto,轉(zhuǎn)換完后會強制添加 rel=nofollow這個屬性。不允許包含圖片。
basicWithImages() 在basic的基礎(chǔ)上增加了圖片的標(biāo)簽:img以及使用src指向http或https類型的圖片鏈接。
none() 只保留文本,其他所有的html內(nèi)容均被刪除
preserveRelativeLinks(booleanpreserve) false(默認(rèn)):不保留相對地址的url;true:保留相對地址的url
relaxed() 允許的標(biāo)簽:a, b, blockquote, br, caption, cite, code, col, colgroup, dd, dl, dt, em, h1, h2, h3, h4, h5, h6, i, img, li, ol, p, pre, q, small, strike, strong, sub, sup, table, tbody, td, tfoot, th, thead, tr, u, ul。結(jié)果不包含標(biāo)簽rel=nofollow,如果需要可以手動添加。
simpleText() 只允許:b, em, i, strong, u。

四、例子

基于springboot

pom.xml依賴

<dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- jsoup -->
    <dependency>
      <groupId>org.jsoup</groupId>
      <artifactId>jsoup</artifactId>
      <version>1.13.1</version>
    </dependency>

    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-lang3</artifactId>
    </dependency>

    <dependency>
      <groupId>commons-io</groupId>
      <artifactId>commons-io</artifactId>
      <version>2.6</version>
    </dependency>

  </dependencies>

HtmlFilter過濾類

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.safety.Whitelist;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.List;

/**
 * HtmlFilter
 *
 * @author 擼小魚
 * Created by lofish@foxmail.com on 2020-04-12
 */
public class HtmlFilter {

  /**
   * 默認(rèn)使用relaxed()
   * 允許的標(biāo)簽: a, b, blockquote, br, caption, cite, code, col, colgroup, dd, dl, dt, em, h1, h2, h3, h4, h5, h6, i, img, li, ol, p, pre, q, small, strike, strong, sub, sup, table, tbody, td, tfoot, th, thead, tr, u, ul。結(jié)果不包含標(biāo)簽rel=nofollow ,如果需要可以手動添加。
   */
  private Whitelist whiteList;


  /**
   * 配置過濾化參數(shù),不對代碼進行格式化
   */
  private Document.OutputSettings outputSettings;


  private HtmlFilter() {
  }

  /**
   * 靜態(tài)創(chuàng)建HtmlFilter方法
   * @param whiteList 白名單標(biāo)簽
   * @param pretty 是否格式化
   * @return HtmlFilter
   */
  public static HtmlFilter create(Whitelist whiteList, boolean pretty) {
    HtmlFilter filter = new HtmlFilter();
    if (whiteList == null) {
      filter.whiteList = Whitelist.relaxed();
    }
    filter.outputSettings = new Document.OutputSettings().prettyPrint(pretty);
    return filter;
  }

  /**
   * 靜態(tài)創(chuàng)建HtmlFilter方法
   * @return HtmlFilter
   */
  public static HtmlFilter create() {
    return create(null, false);
  }

  /**
   * 靜態(tài)創(chuàng)建HtmlFilter方法
   * @param whiteList 白名單標(biāo)簽
   * @return HtmlFilter
   */
  public static HtmlFilter create(Whitelist whiteList) {
    return create(whiteList, false);
  }

  /**
   * 靜態(tài)創(chuàng)建HtmlFilter方法
   * @param excludeTags 例外的特定標(biāo)簽
   * @param includeTags 需要過濾的特定標(biāo)簽
   * @param pretty   是否格式化
   * @return HtmlFilter
   */
  public static HtmlFilter create( List<String> excludeTags,List<String> includeTags, boolean pretty) {
    HtmlFilter filter = create(null, pretty);
    //要過濾的標(biāo)簽
    if (includeTags != null && !includeTags.isEmpty()) {
      String[] tags = (String[]) includeTags.toArray(new String[0]);
      filter.whiteList.removeTags(tags);
    }
    //例外標(biāo)簽
    if (excludeTags != null && !excludeTags.isEmpty()) {
      String[] tags = (String[]) excludeTags.toArray(new String[0]);
      filter.whiteList.addTags(tags);
    }
    return filter;
  }


  /**
   * 靜態(tài)創(chuàng)建HtmlFilter方法
   * @param excludeTags 例外的特定標(biāo)簽
   * @param includeTags 需要過濾的特定標(biāo)簽
   * @return HtmlFilter
   */
  public static HtmlFilter create(List<String> excludeTags,List<String> includeTags) {
    return create( includeTags, excludeTags, false );
  }

  /**
   * @param content 需要過濾內(nèi)容
   * @return 過濾后的String
   */
  public String clean(String content) {
    return Jsoup.clean(content, "", this.whiteList, this.outputSettings);

  }

  public static void main(String[] args) throws FileNotFoundException, IOException {
    String text = "<a href=\"http://www.baidu.com/a\" onclick=\"alert(1);\"></a><script>alert(0);</script><b style=\"xxx\" onclick=\"<script>alert(0);</script>\">abc</>";
    System.out.println(HtmlFilter.create().clean(text));
  }
}

XssFilter過濾器

import org.apache.commons.lang3.StringUtils;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * XssFilter
 *
 * @author 擼小魚
 * Created by lofish@foxmail.com on 2020-04-12
 */
public class XssFilter implements Filter {

  /**
   * 例外urls
   */
  private List<String> excludeUrls = new ArrayList<>();

  /**
   * 例外標(biāo)簽
   */
  private List<String> excludeTags = new ArrayList<>();

  /**
   * 需要過濾標(biāo)簽
   */
  private List<String> includeTags = new ArrayList<>();

  /**
   * 開關(guān)
   */
  public boolean enabled = false;

  /**
   * 編碼
   */
  private String encoding = "UTF-8";

  @Override
  public void init(FilterConfig filterConfig) throws ServletException {
    String enabledStr = filterConfig.getInitParameter("enabled");
    String excludeUrlStr = filterConfig.getInitParameter("urlPatterns");
    String excludeTagStr = filterConfig.getInitParameter("excludes");
    String includeTagStr = filterConfig.getInitParameter("includes");
    String encodingStr = filterConfig.getInitParameter("encoding");

    if (StringUtils.isNotEmpty(excludeUrlStr)) {
      String[] url = excludeUrlStr.split(",");
      Collections.addAll(this.excludeUrls, url);
    }

    if (StringUtils.isNotEmpty(excludeTagStr)) {
      String[] url = excludeTagStr.split(",");
      Collections.addAll(this.excludeTags, url);
    }

    if (StringUtils.isNotEmpty(includeTagStr)) {
      String[] url = includeTagStr.split(",");
      Collections.addAll(this.includeTags, url);
    }

    if (StringUtils.isNotEmpty(enabledStr)) {
      this.enabled = Boolean.parseBoolean(enabledStr);
    }

    if (StringUtils.isNotEmpty(encodingStr)) {
      this.encoding = encodingStr;
    }

  }

  @Override
  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    HttpServletRequest req = (HttpServletRequest) request;
    HttpServletResponse resp = (HttpServletResponse) response;
    if (handleExcludeUrls(req, resp)) {
      chain.doFilter(request, response);
      return;
    }

    XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) request, encoding, excludeTags, includeTags );
    chain.doFilter(xssRequest, response);

  }

  private boolean handleExcludeUrls(HttpServletRequest request, HttpServletResponse response) {
    if (!enabled) {
      return true;
    }
    if (excludeUrls == null || excludeUrls.isEmpty()) {
      return false;
    }
    String url = request.getServletPath();
    for (String pattern : excludeUrls) {
      Pattern p = Pattern.compile("^" + pattern);
      Matcher m = p.matcher(url);
      if (m.find()) {
        return true;
      }
    }
    return false;
  }
}

一般情況下,我們都是通過request的parameter來傳遞參數(shù)。
但是,如果在某些場景下面,通過requestBody體(json等),來傳遞相應(yīng)參數(shù)應(yīng)該怎么辦?
這就要需要我們對request的inputStream來進行來過濾處理了

有個地方需要注意一下的:
servlet中inputStream只能一次讀取,后續(xù)不能再次讀取inputStream。Xss過濾器中讀取了stream之后,后續(xù)如果其他邏輯涉及到inputStream讀取,會拋出異常。那我們就需要想辦法把已經(jīng)讀取的stream,重新放回到請求中。

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;


/**
 * XSS過濾處理
 * @author 擼小魚
 * Created by lofish@foxmail.com
 */
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper{

  HttpServletRequest orgRequest;

  String encoding;

  HtmlFilter htmlFilter;

  private final static String JSON_CONTENT_TYPE = "application/json";

  private final static String CONTENT_TYPE = "Content-Type";


  /**
   * @param request HttpServletRequest
   * @param encoding 編碼
   * @param excludeTags 例外的特定標(biāo)簽
   * @param includeTags 需要過濾的標(biāo)簽
   */
  public XssHttpServletRequestWrapper( HttpServletRequest request, String encoding, List<String> excludeTags, List<String> includeTags ){
    super( request );
    orgRequest = request;
    this.encoding = encoding;
    this.htmlFilter = HtmlFilter.create( excludeTags, includeTags );
  }

  /**
   *
   * @param request HttpServletRequest
   * @param encoding 編碼
   */
  public XssHttpServletRequestWrapper( HttpServletRequest request, String encoding ){
    this( request, encoding, null, null );
  }

  private String xssFilter( String input ){
    return htmlFilter.clean( input );
  }

  @Override
  public ServletInputStream getInputStream() throws IOException{
    // 非json處理
    if( !JSON_CONTENT_TYPE.equalsIgnoreCase( super.getHeader( CONTENT_TYPE ) ) ){
      return super.getInputStream();
    }
    InputStream in = super.getInputStream();
    String body = IOUtils.toString( in, encoding );
    IOUtils.closeQuietly( in );

    //空串處理直接返回
    if( StringUtils.isBlank( body ) ){
      return super.getInputStream();
    }

    // xss過濾
    body = xssFilter( body );
    return new RequestCachingInputStream( body.getBytes( encoding ) );

  }

  @Override
  public String getParameter( String name ){
    String value = super.getParameter( xssFilter( name ) );
    if( StringUtils.isNotBlank( value ) ){
      value = xssFilter( value );
    }
    return value;
  }

  @Override
  public String[] getParameterValues( String name ){
    String[] parameters = super.getParameterValues( name );
    if( parameters == null || parameters.length == 0 ){
      return null;
    }

    for( int i = 0; i < parameters.length; i++ ){
      parameters[i] = xssFilter( parameters[i] );
    }
    return parameters;
  }

  @Override
  public Map<String, String[]> getParameterMap(){
    Map<String, String[]> map = new LinkedHashMap<>();
    Map<String, String[]> parameters = super.getParameterMap();
    for( String key : parameters.keySet() ){
      String[] values = parameters.get( key );
      for( int i = 0; i < values.length; i++ ){
        values[i] = xssFilter( values[i] );
      }
      map.put( key, values );
    }
    return map;
  }

  @Override
  public String getHeader( String name ){
    String value = super.getHeader( xssFilter( name ) );
    if( StringUtils.isNotBlank( value ) ){
      value = xssFilter( value );
    }
    return value;
  }

  /**
   * <b>
   * #獲取最原始的request
   * </b>
   */
  public HttpServletRequest getOrgRequest(){
    return orgRequest;
  }

  /**
   * <b>
   * #獲取最原始的request
   * </b>
   * @param request HttpServletRequest
   */
  public static HttpServletRequest getOrgRequest( HttpServletRequest request ){
    if( request instanceof XssHttpServletRequestWrapper ){
      return ((XssHttpServletRequestWrapper) request).getOrgRequest();
    }
    return request;
  }

  /**
   * <pre>
   * servlet中inputStream只能一次讀取,后續(xù)不能再次讀取inputStream
   * xss過濾body后,重新把流放入ServletInputStream中
   * </pre>
   */
  private static class RequestCachingInputStream extends ServletInputStream {
    private final ByteArrayInputStream inputStream;
    public RequestCachingInputStream(byte[] bytes) {
      inputStream = new ByteArrayInputStream(bytes);
    }

    @Override
    public int read() throws IOException {
      return inputStream.read();
    }

    @Override
    public boolean isFinished() {
      return inputStream.available() == 0;
    }

    @Override
    public boolean isReady() {
      return true;
    }

    @Override
    public void setReadListener( ReadListener readListener ){
    }
  }
}

springboot2.2.4.RELEASE中注冊Filter

@Configuration
public class XssFilterConfig {

  @Value("${xss.enabled:true}")
  private String enabled;

  @Value("${xss.excludes:}")
  private String excludes;

  @Value("${xss.includes$:}")
  private String includes;

  @Value("${xss.urlPatterns:/*}")
  private String urlPatterns;

  @Bean
  public FilterRegistrationBean<XssFilter> xssFilterRegistrationBean() {
    FilterRegistrationBean<XssFilter> registration = new FilterRegistrationBean<>();
    registration.setDispatcherTypes(DispatcherType.REQUEST);
    registration.setFilter(new XssFilter());
    registration.addUrlPatterns(urlPatterns.split(","));
    registration.setName("XssFilter");
    registration.setOrder(Integer.MAX_VALUE);
    Map<String, String> initParameters = new HashMap<String, String>();
    initParameters.put("excludes", excludes);
    initParameters.put("includes", excludes);
    initParameters.put("enabled", enabled);
    registration.setInitParameters(initParameters);
    return registration;
  }
}

測試

http://localhost:8080/demo/th/xss?abc=%3Ca%20href=%22http://www.baidu.com/a%22%20onclick=%22alert(1);%22%3Eabc%3C/a%3E%3Cscript%3Ealert(0);%3C/script%3E&abc=%3Cb%20style=%22xxx%22%20onclick=%22%3Cscript%3Ealert(0);%3C/script%3E%22%3Eabc%3C/%3E

到此這篇關(guān)于springboot2.x使用Jsoup防XSS攻擊的實現(xiàn)的文章就介紹到這了,更多相關(guān)springboot2.x防XSS攻擊內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot連接MySql數(shù)據(jù)庫的原理及代碼示例

    SpringBoot連接MySql數(shù)據(jù)庫的原理及代碼示例

    SpringBoot是一款流行的Java開發(fā)框架,它可以輕松地連接各種類型的數(shù)據(jù)庫,包括關(guān)系型數(shù)據(jù)庫和非關(guān)系型數(shù)據(jù)庫,本文將介紹SpringBoot是如何連接數(shù)據(jù)庫的,包括其原理和代碼示例,需要的朋友可以參考下
    2023-07-07
  • Java設(shè)計模式中的橋接模式

    Java設(shè)計模式中的橋接模式

    這篇文章主要介紹了Java設(shè)計模式中的橋接模式,其是一種結(jié)構(gòu)型設(shè)計模式,是指將實現(xiàn)與抽象放在兩個不同的類層次中,使兩個層次可以獨立改變
    2022-07-07
  • 淺談一下RabbitMQ、Kafka和RocketMQ消息中間件對比

    淺談一下RabbitMQ、Kafka和RocketMQ消息中間件對比

    這篇文章主要介紹了淺談一下RabbitMQ、Kafka和RocketMQ消息中間件對比,消息中間件屬于分布式系統(tǒng)中一個字系統(tǒng),關(guān)注于數(shù)據(jù)的發(fā)送和接收,利用高效可靠的異步信息傳遞機制對分布式系統(tǒng)中的其余各個子系統(tǒng)進行集成,需要的朋友可以參考下
    2023-05-05
  • 手工體驗smtp和pop3協(xié)議 郵件實現(xiàn)詳解(二)

    手工體驗smtp和pop3協(xié)議 郵件實現(xiàn)詳解(二)

    POP3/IMAP協(xié)議定義了郵件客戶端軟件和POP3郵件服務(wù)器的通信規(guī)則,這篇文章我們就來手工體驗SMTP和POP3協(xié)議的奧秘,感興趣的小伙伴們可以參考一下
    2017-10-10
  • java實現(xiàn)JSON字符串格式化輸出

    java實現(xiàn)JSON字符串格式化輸出

    這篇文章主要為大家詳細介紹了如何使用java實現(xiàn)JSON字符串格式化輸出,文中的示例代碼講解詳細,具有一定的借鑒價值,感興趣的小伙伴可以了解下
    2024-01-01
  • SpringBoot集成Spring security JWT實現(xiàn)接口權(quán)限認(rèn)證

    SpringBoot集成Spring security JWT實現(xiàn)接口權(quán)限認(rèn)證

    這篇文章主要介紹了SpringBoot集成Spring security JWT實現(xiàn)接口權(quán)限認(rèn)證,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-04-04
  • 利用java讀取web項目中json文件為map集合方法示例

    利用java讀取web項目中json文件為map集合方法示例

    這篇文章主要給大家介紹了關(guān)于利用java讀取web項目中json文件為map集合的相關(guān)資料,文中通過示例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起看看吧。
    2017-08-08
  • 關(guān)于Maven如何構(gòu)建生命周期

    關(guān)于Maven如何構(gòu)建生命周期

    這篇文章主要介紹了關(guān)于Maven如何構(gòu)建生命周期,Maven構(gòu)建生命周期描述的是一次構(gòu)建過程經(jīng)歷經(jīng)歷了多少個事件,需要的朋友可以參考下
    2023-04-04
  • java Lambda表達式的使用心得

    java Lambda表達式的使用心得

    這篇文章主要介紹了java Lambda表達式的使用心得,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • JavaWeb文件上傳下載實例講解(酷炫的文件上傳技術(shù))

    JavaWeb文件上傳下載實例講解(酷炫的文件上傳技術(shù))

    在Web應(yīng)用系統(tǒng)開發(fā)中,文件上傳功能是非常常用的功能,今天來主要講講JavaWeb中的文件上傳功能的相關(guān)技術(shù)實現(xiàn),本文給大家介紹的非常詳細,具有參考借鑒價值,感興趣的朋友一起看看吧
    2016-11-11

最新評論

郧西县| 峨边| 宿州市| 徐闻县| 滕州市| 资阳市| 江达县| 景宁| 尖扎县| 安国市| 沙田区| 广南县| 陕西省| 卓尼县| 镇赉县| 弥勒县| 黄陵县| 连州市| 阿克苏市| 白城市| 毕节市| 铁岭市| 视频| 孟州市| 德钦县| 高雄市| 错那县| 车致| 枣阳市| 晴隆县| 桂平市| 彭山县| 渭源县| 平度市| 荣成市| 德兴市| 宁国市| 鄂尔多斯市| 抚顺市| 江安县| 三门县|