XSS 防禦方法總結

來源:互聯網
上載者:User

標籤:

1. XSS攻擊原理

XSS原稱為CSS(Cross-Site Scripting),因為和層疊樣式表(Cascading Style Sheets)重名,所以改稱為XSS(X有未知的含義,還有擴充的含義)。XSS攻擊涉及到三方:攻擊者,使用者,web server。使用者通過瀏覽器來訪問web server上的網頁,XSS攻擊就是攻擊者通過各種辦法,在使用者訪問的網頁中插入自己的指令碼,讓其在使用者訪問網頁時在瀏覽器中進行執行。攻擊者通過插入的指令碼的執行,來獲得使用者的資訊,比如cookie,發送到攻擊者自己的網站。所以稱為跨站指令碼攻擊。XSS可以分為反射型XSS和持久性XSS。(一句話,XSS就是在使用者的瀏覽器中執行攻擊者自己定製的指令碼。)

1.1 反射型XSS

反射性XSS,也就是非持久性XSS。使用者點擊攻擊連結,伺服器解析後響應,在返回的響應內容中出現攻擊者的XSS代碼,被瀏覽器執行。一來一去,XSS攻擊指令碼被web server反射回來給瀏覽器執行,所以稱為反射型XSS。

特點:

1> XSS攻擊代碼非持久性,也就是沒有儲存在web server中,而是出現在URL地址中;

2> 非持久性,那麼攻擊方式就不同了。一般是攻擊者通過郵件,聊天軟體等等方式發送攻擊URL,然後使用者點擊來達到攻擊的;

1.2 持久型XSS

區別就是XSS惡意代碼儲存在web server中,這樣,每一個訪問特定網頁的使用者,都會被攻擊。

特點:

1> XSS攻擊代碼儲存於web server上;

2> 攻擊者,一般是通過網站的留言、評論、部落格、日誌等等功能(所有能夠向web server輸入內容的地方),將攻擊代碼儲存到web server上的;

2. XSS 存在的原因

XSS 存在的根本原因是,對URL中的參數,對使用者輸入提交給web server的內容,沒有進行充分的過濾。如果我們能夠在web程式中,對使用者提交的URL中的參數,和提交的所有內容,進行充分的過濾,將所有的不合法的參數和輸入內容過濾掉,那麼就不會導致“在使用者的瀏覽器中執行攻擊者自己定製的指令碼”。

但是,其實充分而完全的過濾,實際上是無法實現的。因為攻擊者有各種各樣的神奇的,你完全想象不到的方式來繞過伺服器端的過濾,最典型的就是對URL和參數進行各種的編碼,比如escape, encodeURI, encodeURIComponent, 16進位,10進位,來繞過XSS過濾。那麼我們如何來防禦XSS呢?

3. XSS 攻擊的防禦

XSS防禦的總體思路是:對輸入(和URL參數)進行過濾,對輸出進行編碼

也就是對提交的所有內容進行過濾,對url中的參數進行過濾,過濾掉會導致指令碼執行的相關內容;然後對動態輸出到頁面的內容進行html編碼,使指令碼無法在瀏覽器中執行。雖然對輸入過濾可以被繞過,但是也還是會攔截很大一部分的XSS攻擊。

3.1 對輸入和URL參數進行過濾(白名單和黑名單)

下面貼出一個常用的XSS filter的實現代碼:

public class XssFilter implements Filter {    public void init(FilterConfig config) throws ServletException {}    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)             throws IOException, ServletException {        XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest)request);        chain.doFilter(xssRequest, response);    }    public void destroy() {}}
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {    HttpServletRequest orgRequest = null;    public XssHttpServletRequestWrapper(HttpServletRequest request) {        super(request);        orgRequest = request;    }    /**     * 覆蓋getParameter方法,將參數名和參數值都做xss過濾。<br/>     * 如果需要獲得原始的值,則通過super.getParameterValues(name)來擷取<br/>     * getParameterNames,getParameterValues和getParameterMap也可能需要覆蓋     */    @Override    public String getParameter(String name) {        String value = super.getParameter(xssEncode(name));        if (value != null) {            value = xssEncode(value);        }        return value;    }    /**     * 覆蓋getHeader方法,將參數名和參數值都做xss過濾。<br/>     * 如果需要獲得原始的值,則通過super.getHeaders(name)來擷取<br/>     * getHeaderNames 也可能需要覆蓋     */    @Override    public String getHeader(String name) {        String value = super.getHeader(xssEncode(name));        if (value != null) {            value = xssEncode(value);        }        return value;    }    /**     * 將容易引起xss漏洞的半形字元直接替換成全形字元     *     * @param s     * @return     */    private static String xssEncode(String s) {        if (s == null || s.isEmpty()) {            return s;        }        StringBuilder sb = new StringBuilder(s.length() + 16);        for (int i = 0; i < s.length(); i++) {            char c = s.charAt(i);            switch (c) {            case ‘>‘:                sb.append(‘>‘);// 全形大於符號                break;            case ‘<‘:                sb.append(‘<‘);// 全形小於符號                break;            case ‘\‘‘:                sb.append(‘‘‘);// 全形單引號                break;            case ‘\"‘:                sb.append(‘“‘);// 全形雙引號                break;            case ‘&‘:                sb.append(‘&‘);// 全形                break;            case ‘\\‘:                sb.append(‘\‘);// 全形斜線                break;            case ‘#‘:                sb.append(‘#‘);// 全形井號                break;            case ‘%‘:    // < 字元的 URL 編碼形式表示的 ASCII 字元(十六進位格式) 是: %3c                processUrlEncoder(sb, s, i);                break;            default:                sb.append(c);                break;            }        }        return sb.toString();    }    public static void processUrlEncoder(StringBuilder sb, String s, int index){        if(s.length() >= index + 2){            if(s.charAt(index+1) == ‘3‘ && (s.charAt(index+2) == ‘c‘ || s.charAt(index+2) == ‘C‘)){    // %3c, %3C                sb.append(‘<‘);                return;            }            if(s.charAt(index+1) == ‘6‘ && s.charAt(index+2) == ‘0‘){    // %3c (0x3c=60)                sb.append(‘<‘);                return;            }                        if(s.charAt(index+1) == ‘3‘ && (s.charAt(index+2) == ‘e‘ || s.charAt(index+2) == ‘E‘)){    // %3e, %3E                sb.append(‘>‘);                return;            }            if(s.charAt(index+1) == ‘6‘ && s.charAt(index+2) == ‘2‘){    // %3e (0x3e=62)                sb.append(‘>‘);                return;            }        }        sb.append(s.charAt(index));    }    /**     * 擷取最原始的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;    }}

然後在web.xml中配置該filter:

    <filter>        <filter-name>xssFilter</filter-name>        <filter-class>com.xxxxxx.filter.XssFilter</filter-class>    </filter>    <filter-mapping>        <filter-name>xssFilter</filter-name>        <url-pattern>/*</url-pattern>    </filter-mapping>

主要的思路就是將容易導致XSS攻擊的邊角字元替換成全形字元。< 和 > 是指令碼執行和各種html標籤需要的,比如 <script>,& 和 # 以及 % 在對URL編碼試圖繞過XSS filter時,會出現。我們說對輸入的過濾分為白名單和黑名單。上面的XSS filter就是一種黑名單的過濾,黑名單就是列出不能出現的對象的清單,一旦出現就進行處理。還有一種白名單的過濾,白名單就是列出可被接受的內容,比如規定所有的輸入只能是“大小寫26個英文字母和10個數字,還有-和_”,所有其他的輸入都是非法的,會被拋棄掉。很顯然如此嚴格的白名單是可以100%攔截所有的XSS攻擊的。但是現實情況一般是不能進行如此嚴格的白名單過濾的。

對於輸入,處理使用XSS filter之外,對於每一個輸入,在用戶端和伺服器端還要進行各種驗證,驗證是否合法字元,長度是否合法,格式是否正確。在用戶端和服務端都要進行驗證,因為用戶端的驗證很容易被繞過。其實這種驗證也分為了黑名單和白名單。黑名單的驗證就是不能出現某些字元,白名單的驗證就是只能出現某些字元。盡量使用白名單。

3.2 對輸出進行編碼

在輸出資料之前對潛在的威脅的字元進行編碼、轉義是防禦XSS攻擊十分有效措施。如果使用好的話,理論上是可以防禦住所有的XSS攻擊的。

對所有要動態輸出到頁面的內容,通通進行相關的編碼和轉義。當然轉義是按照其輸出的上下文環境來決定如何轉義的。

1> 作為body文本輸出,作為html標籤的屬性輸出:

比如:<span>${username}</span>, <p><c:out value="${username}"></c:out></p>

<input type="text" value="${username}" />

此時的轉義規則如下:

< 轉成 &lt;

> 轉成 &gt;

& 轉成 &amp;

" 轉成 &quot;

‘ 轉成 &#39

2> javascript事件

<input type="button" onclick=‘go_to_url("${myUrl}");‘ />

除了上面的那些轉義之外,還要附加上下面的轉義:

\ 轉成 \\

/ 轉成 \/

; 轉成 ;(全形;)

3> URL屬性

如果 <script>, <style>, <imt> 等標籤的 src 和 href 屬性值為動態內容,那麼要確保這些url沒有執行惡意串連。

確保:href 和 src 的值必須以 http://開頭,白名單方式;不能有10進位和16進位編碼字元。

4. 總結下

XSS攻擊存取方法:對輸入(和URL參數)進行過濾,對輸出進行編碼;白名單和黑名單結合;

XSS 防禦方法總結

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.