springboot2.x使用Jsoup防 XSS 攻擊

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

一揣钦、什么是XSS?

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

二寻狂、準(zhǔn)則

  1. 永遠(yuǎn)不要相信用戶的輸入和請(qǐng)求的參數(shù)(包括文字眼刃、上傳等一切內(nèi)容)
  2. 參考第1條

三、實(shí)現(xiàn)做法

結(jié)合具體業(yè)務(wù)場(chǎng)景,對(duì)相應(yīng)內(nèi)容進(jìn)行過(guò)濾图呢,這里使用Jsoup褐桌。

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

過(guò)濾前:

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

過(guò)濾后:

<b>abc</b>

Whitelist主要方法說(shuō)明

方法 說(shuō)明
addAttributes(String tag, String... attributes) 給標(biāo)簽添加屬性。Tag是屬性名献烦,keys對(duì)應(yīng)的是一個(gè)個(gè)屬性值滓窍。例如:addAttributes("a", "href", "class")表示:給標(biāo)簽a添加href和class屬性,即允許標(biāo)簽a包含href和class屬性巩那。如果想給每一個(gè)標(biāo)簽添加一組屬性吏夯,使用:all。例如:addAttributes(":all", "class").即給每個(gè)標(biāo)簽添加class屬性即横。
addEnforcedAttribute(String tag, String attribute, String value) 給標(biāo)簽添加強(qiáng)制性屬性噪生,如果標(biāo)簽已經(jīng)存在了要添加的屬性,則覆蓋原有值东囚。tag:標(biāo)簽跺嗽;key:標(biāo)簽的鍵;value:標(biāo)簽的鍵對(duì)應(yīng)的值页藻。例如:addEnforcedAttribute("a", "rel", "nofollow")表示<a href="..." 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)換完后會(huì)強(qiáng)制添加 rel=nofollow這個(gè)屬性废境。不允許包含圖片畜挨。
basicWithImages() 在basic的基礎(chǔ)上增加了圖片的標(biāo)簽:img以及使用src指向http或https類型的圖片鏈接筒繁。
none() 只保留文本,其他所有的html內(nèi)容均被刪除
preserveRelativeLinks(booleanpreserve) false(默認(rèn)):不保留相對(duì)地址的url巴元;true:保留相對(duì)地址的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,如果需要可以手動(dòng)添加务冕。
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過(guò)濾類

package net.lofish.xpra.xss;

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 擼小魚(yú)
 * 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 ,如果需要可以手動(dòng)添加落恼。
     */
    private Whitelist whiteList;


    /**
     * 配置過(guò)濾化參數(shù),不對(duì)代碼進(jìn)行格式化
     */
    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 需要過(guò)濾的特定標(biāo)簽
     * @param pretty      是否格式化
     * @return HtmlFilter
     */
    public static HtmlFilter create( List<String> excludeTags,List<String> includeTags, boolean pretty) {
        HtmlFilter filter = create(null, pretty);
        //要過(guò)濾的標(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 需要過(guò)濾的特定標(biāo)簽
     * @return HtmlFilter
     */
    public static HtmlFilter create(List<String> excludeTags,List<String> includeTags) {
        return create( includeTags, excludeTags, false );
    }

    /**
     * @param content 需要過(guò)濾內(nèi)容
     * @return 過(guò)濾后的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過(guò)濾器

package net.lofish.xpra.xss;

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 擼小魚(yú)
 * 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<>();

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

    /**
     * 開(kāi)關(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("excludeUrls");
        String excludeTagStr = filterConfig.getInitParameter("excludeTagStr");
        String includeTagStr = filterConfig.getInitParameter("excludeTagStr");
        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(excludeTagStr)) {
            String[] url = excludeTagStr.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;
    }
}

一般情況下箩退,我們都是通過(guò)request的parameter來(lái)傳遞參數(shù)。
但是佳谦,如果在某些場(chǎng)景下面戴涝,通過(guò)requestBody體(json等),來(lái)傳遞相應(yīng)參數(shù)應(yīng)該怎么辦钻蔑?
這就要需要我們對(duì)request的inputStream來(lái)進(jìn)行來(lái)過(guò)濾處理了

有個(gè)地方需要注意一下的:
servlet中inputStream只能一次讀取啥刻,后續(xù)不能再次讀取inputStream。Xss過(guò)濾器中讀取了stream之后咪笑,后續(xù)如果其他邏輯涉及到inputStream讀取可帽,會(huì)拋出異常。那我們就需要想辦法把已經(jīng)讀取的stream窗怒,重新放回到請(qǐng)求中映跟。

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過(guò)濾處理
 * @author 擼小魚(yú)
 * 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 需要過(guò)濾的標(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過(guò)濾
        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過(guò)濾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中注冊(cè)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;
    }
}

測(cè)試

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
image
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末努隙,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子辜昵,更是在濱河造成了極大的恐慌荸镊,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,525評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件堪置,死亡現(xiàn)場(chǎng)離奇詭異贷洲,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)晋柱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)优构,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人雁竞,你說(shuō)我怎么就攤上這事钦椭∨《睿” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,862評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵彪腔,是天一觀的道長(zhǎng)侥锦。 經(jīng)常有香客問(wèn)我,道長(zhǎng)德挣,這世上最難降的妖魔是什么恭垦? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,728評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮格嗅,結(jié)果婚禮上番挺,老公的妹妹穿的比我還像新娘。我一直安慰自己屯掖,他們只是感情好玄柏,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,743評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著贴铜,像睡著了一般粪摘。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上绍坝,一...
    開(kāi)封第一講書(shū)人閱讀 51,590評(píng)論 1 305
  • 那天徘意,我揣著相機(jī)與錄音,去河邊找鬼轩褐。 笑死映砖,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的灾挨。 我是一名探鬼主播邑退,決...
    沈念sama閱讀 40,330評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼劳澄!你這毒婦竟也來(lái)了地技?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,244評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤秒拔,失蹤者是張志新(化名)和其女友劉穎莫矗,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體砂缩,經(jīng)...
    沈念sama閱讀 45,693評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡作谚,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,885評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了庵芭。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片妹懒。...
    茶點(diǎn)故事閱讀 40,001評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖双吆,靈堂內(nèi)的尸體忽然破棺而出眨唬,到底是詐尸還是另有隱情会前,我是刑警寧澤,帶...
    沈念sama閱讀 35,723評(píng)論 5 346
  • 正文 年R本政府宣布匾竿,位于F島的核電站瓦宜,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏岭妖。R本人自食惡果不足惜临庇,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,343評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望昵慌。 院中可真熱鬧假夺,春花似錦、人聲如沸废离。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,919評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)蜻韭。三九已至,卻和暖如春柿扣,著一層夾襖步出監(jiān)牢的瞬間肖方,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,042評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工未状, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留俯画,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,191評(píng)論 3 370
  • 正文 我出身青樓司草,卻偏偏與公主長(zhǎng)得像艰垂,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子埋虹,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,955評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 服務(wù)器處理富文本編輯器提交的內(nèi)容時(shí), 因排版的需求不能對(duì) HTML 標(biāo)簽進(jìn)行轉(zhuǎn)義, 但為了防止 XSS 攻擊, 又...
    jnil閱讀 16,505評(píng)論 0 9
  • 淺談XSS—字符編碼和瀏覽器解析原理 XSS簡(jiǎn)介 XSS攻擊全稱跨站腳本攻擊猜憎,是為不和層疊樣式表(Cascadin...
    Smi1e_閱讀 4,638評(píng)論 2 3
  • Beautiful Soup是一個(gè)可以從HTML或XML文件中提取數(shù)據(jù)的Python庫(kù).它能夠通過(guò)你喜歡的轉(zhuǎn)換器實(shí)...
    LitOrange閱讀 5,279評(píng)論 0 4
  • Jsoup官方文檔中文版 解析和遍歷一個(gè)HTML文檔 一個(gè)文檔的對(duì)象模型 文檔由多個(gè)Elements和TextNo...
    大灰狼zz閱讀 7,023評(píng)論 0 4
  • 之前積累了XSS 有一段時(shí)間,因?yàn)槟壳伴_(kāi)始了一件有趣的工程搔课,需要整合非常多的知識(shí)胰柑,其中Web 安全這一塊出現(xiàn)最多的...
    刀背藏身閱讀 9,063評(píng)論 0 16