溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何對瀏覽器解析和XSS的深度探究

發(fā)布時間:2021-12-27 13:49:10 來源:億速云 閱讀:195 作者:柒染 欄目:網(wǎng)絡管理

這篇文章將為大家詳細講解有關如何對瀏覽器解析和XSS的深度探究,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關知識有一定的了解。

一、需要了解的一些基本知識

   為什么要進行編碼?

   主要是因為某些數(shù)據(jù)不適合傳輸。原因多種多樣,如 Size 過大,包含隱私數(shù)據(jù),另外重要的一點就是有些字符會引起歧義。

對于 URL: &用于分割多個參數(shù),倘若有某個參數(shù)鍵值為 name=v&lue,就會因為 name 參數(shù)的值 v&lue 中攜帶了&而造成歧義。因此需要對&進行 URL 編碼。url編碼后,服務端會把緊跟在“%”后的字節(jié)當成普通的字節(jié),就是不會把它當成各個參數(shù)或鍵值對的分隔符。

對于 HTML: 當瀏覽器遇到<會識別為元素定義的開始,>會識別為元素的結(jié)束。倘若有<div id="1>" ></div>,由于標簽的屬性值攜帶了>,同樣會造成歧義。因此需要屬性值的>需要 進行 HTML 編碼,即使用字符實體。

二、三種編碼

2.1HTML 編碼(字符實體)

字符實體是一個預先定義好的轉(zhuǎn)義序列。 字符實體兩種表示方法:

1、 字符實體以&開頭+預先定義的實體名稱+;分號結(jié)束,如“<”的實體名稱為&amplt;

2、 字符實體還可以以&開頭+#符號+字符在 ASCII 對應的十進制數(shù)字+;分號結(jié)束,如<的實體編號為&amp#60;

字符都是有實體編號的,但有些字符是沒有實體名稱

顯示結(jié)果 描述            實體名稱            實體編號            

空格&ampnbsp;&amp#160;
小于號&amplt;&amp#60;
大于號&ampgt;&amp#62;
&和號&ampamp;&amp#38;
"引號&ampquot;&amp#34;
'撇號 &ampapos; (IE不支持)&amp#39;
分(cent)&ampcent;&amp#162;
鎊(pound)&amppound;&amp#163;
元(yen)&ampyen;&amp#165;
歐元(euro)&ampeuro;&amp#8364;
§小節(jié)&ampsect;&amp#167;
?版權(copyright)&ampcopy;&amp#169;
?注冊商標&ampreg;&amp#174;
?商標&amptrade;&amp#8482;
×乘號&amptimes;&amp#215;
÷除號&ampdivide;&amp#247;

2.2 JavaScript 編碼

最常用的,如\uXXXX 這種寫法的Unicode 轉(zhuǎn)義序列,表示一個字符,其中 XXXX 表示一個 16 進制數(shù)字,如<的Unicode 編碼為\u003c。

2.3 URL 編碼

RFC3986 文檔規(guī)定,URL 中只允許包含英文字母(a-zA-Z)、數(shù)字(0-9)、-_.~4 個特殊字 符以及所有保留字符。 RFC3986 中指定了以下字符為保留字符:! * ' ( ) ; : @ & = + $ , / ? # [ ]

編碼方式 %加字符在 ASCII 碼表中的十六進制值。例如,/在 ASCII 碼表中十六進制為 0x2f,那么它對應的 URL 編碼為%2f。

JavaScript 中提供了 3 個函數(shù)用來對 URL 編碼以得到合法的 URL:

1、 escape()

2、 encodeURI()

3、 encodeURIComponent()

三、瀏覽器解碼規(guī)則

如何對瀏覽器解析和XSS的深度探究

瀏覽器無論什么情況都會遵守一個這樣的解碼規(guī)則:

1、 HTML 解析器對 HTML 文檔進行解析,完成 HTML 解碼并且創(chuàng)建 DOM 樹

2、 JavaScript 或者 CSS 解析器對內(nèi)聯(lián)腳本進行解析,完成 JS、CSS 解碼

3、 URL 解碼會根據(jù) URL 所在的順序不同而在 JS 解碼前或者解碼后

3.1HTML 解析器

3.1.1HTML 中有五類元素:

1、 空元素(Voidelements),有 area、base、br、col、command、embed、hr、img、input、 keygen、link、meta、param、source、track、wbr

2、 原始文本元素(Raw textelements),有<script>和<style>

3、 RCDATA 元素(RCDATA elements),有<textarea>和<title>

4、 外部元素(Foreignelements),例如 MathML 命名空間或者 SVG 命名空間的元素

5、 基本元素(Normal elements),即除了以上 4 種元素以外的元素

五類元素的區(qū)別如下:

1、 空元素,不能容納任何內(nèi)容(因為它們沒有閉合標簽,沒有內(nèi)容能夠放在開始標簽和閉合標簽中間)。

2、 原始文本元素,可以容納文本。

3、 RCDATA 元素,可以容納文本和字符引用。

4、 外部元素,可以容納文本、字符引用、CDATA 段、其他元素和注釋

5、 基本元素,可以容納文本、字符引用、其他元素和注釋

3.1.2 HTML編解碼

如何對瀏覽器解析和XSS的深度探究

HTML 解析器以狀態(tài)機的方式運行,它從文檔輸入流中消耗字符并根據(jù)其轉(zhuǎn)換規(guī)則轉(zhuǎn)換到不同的狀態(tài)。

示例:

<html><body>Hello world</body></html>

1、 初始狀態(tài)為"DataState",當遇到"<"字符,狀態(tài)變?yōu)?quot;Tag open state",讀取一個 a-z 的字符將產(chǎn)生一個開始標簽符號,狀態(tài)相應變?yōu)?quot;Tag name state",一直保持這個狀態(tài)直到讀取到">",每個字符都附加到這個符號名上,例子中創(chuàng)建的是一個 html 符號。

2、 當讀取到">",當前的符號就完成了,此時,狀態(tài)回到"Data state","<body>"重復這一處理過程。到這里,html 和 body 標簽都識別出來了?,F(xiàn)在,回到"Data state",讀取"Helloworld"中的字符"H"將創(chuàng)建并識別出一個字符符號,這里會為"Hello world"中的每個字符 生成一個字符符號。

3、 這樣直到遇到"</body>"中的"<"?,F(xiàn)在,又回到了"Tag openstate",讀取下一個字符"/" 將創(chuàng)建一個閉合標簽符號,并且狀態(tài)轉(zhuǎn)移到"Tag name state",還是保持這一狀態(tài),直到 遇到">"。然后,產(chǎn)生一個新的標簽符號并回到"Data state"。后面的"</html>"將和 "</body>"一樣處理。

總結(jié):當HTML 解析器處于數(shù)據(jù)狀態(tài)(DataState)、RCDATA 狀態(tài)(RCDATA State)、屬性值狀態(tài)(Attribute Value State)時,字符實體會被解碼為對應的字符。

示例:

<div>&amp#60;img src=x onerror=alert(4)&amp#62;</div>

<和>被編碼為字符實體&amp#60;和&amp#62;。 當 HTML 解析器解析完<div>時,會進入數(shù)據(jù)狀態(tài)(Data State)并發(fā)布標簽令牌。接著解析到實體&amp#60;時因為處在數(shù)據(jù)狀態(tài)(Data State)就會對實體進行解碼為<,后面 的&amp#62;同樣道理被解碼為>。

這里會有個問題,被解碼后,img是否會被解析為 HTML 標簽而導致 JS 執(zhí)行呢?

答案是否定的。因為解析器在使用字符引用后不會轉(zhuǎn)換到標簽打開狀態(tài)(Tag OpenState),不進入標簽打開狀態(tài)就不會被發(fā)布為 HTML 標簽。因此,不會創(chuàng)建新 HTML 標簽, 只會將其作為數(shù)據(jù)來處理。 這也是為什么我們可以使用字符實體來避免用戶不安全輸入導致 XSS 的原因。

3.1.3 原始文本元素(Raw text elements)

在 HTML 中,屬于 Rawtext elements 的標簽有兩個:script、style。在 Raw text elements 類型標簽下的所有內(nèi)容塊都屬于該標簽。

存在一條特性: Raw text elements 類型標簽下的所有字符實體編碼都不會被 HTML 解碼。HTML 解析器 解析到 script、style 標簽的內(nèi)容塊(數(shù)據(jù))部分時,狀態(tài)會進入 Script Data State,該狀態(tài)并 不在我們前面說的會解碼字符實體的三條狀態(tài)之中。

因此,<script>&amp#97;&amp#108;&amp#101;&amp#114;&#116&amp#40;&amp#57;&amp#41;&amp#59;</script>這樣字符實體并不會被解碼,也就不會執(zhí)行 JS。

3.1.4 RCDATA

在 HTML 中,屬于RCDATAElements的標簽有兩個:textarea、title。

RCDATA Elements 類型的標簽可以包含文本內(nèi)容和字符實體。

解析器解析到 textarea、title標簽的數(shù)據(jù)部分時,狀態(tài)會進入 RCDATA State。

前面我們提到,處于 RCDATA State 狀態(tài)時,字符實體是會被解析器解碼的。

示例:

<textarea>&amp#60;script&amp#62;alert(5)&amp#60;/script&amp#62;</textarea>

<和>被編碼為實體&amp#60;和&amp#62;。 解析器解析到它們時會進行解碼,最終得到<textarea><script>alert(5)</script></textarea>。但是里面的 JS 同樣還是不會被執(zhí) 行,原因還是因為解碼字符實體狀態(tài)機不會進入標簽打開狀態(tài)(TagOpen State),因此里面的<script>并不會被解析為 HTML 標簽。

3.1.5 外部元素(Foreign elements)

來源于 MathML 和 SVG 命名空間

<svg>遵循 XML 和 SVG 的定義

實例:

<script>alert&amp#40;1)</script>

不能彈窗,Raw text elements 類型標簽下的所有字符實體編碼都不會被 HTML 解碼

<svg><script>alert&amp#40;1)</script>

能彈窗,在 XML 中,&amp#40;會被解析成(,在 XML 中實體會自動轉(zhuǎn)義,除了<![CDATA[和]]> 包含的實體

3.2 JavaScript 解析器

形如 \uXXXX 這樣的 Unicode 字符轉(zhuǎn)義序列或 Hex 編碼是否能被解碼需要看情況。 首先,JavaScript 中有三個地方可以出現(xiàn) Unicode 字符轉(zhuǎn)義序列:

1、字符串中(in String)

Unicode 轉(zhuǎn)義序列出現(xiàn)在字符串中時,它只會被解釋為普通字符,而不會破壞字符串的上下文。

例如,<script>alert("\u0031\u0030");</script>

被編碼轉(zhuǎn)義的部分為 10,是字符串,會被正常解碼,JS 代碼也會被執(zhí)行。

2、標識符中(in identifier names)

若 Unicode 轉(zhuǎn)義序列存在于標識符中,即變量名(如函數(shù)名等…),它會被進行解碼。

例如,<script>\u0061\u006c\u0065\u0072\u0074(10);</script>

被編碼轉(zhuǎn)義的部分為 alert 字符,是函數(shù)名,屬于在標識符中的情況,因此會被正常解碼,JS 代碼也會被執(zhí)行。

3、控制字符中(in control characters)

若 Unicode 轉(zhuǎn)義序列存在于控制字符中,那么它會被解碼但不會被解釋為控制字符,而會被解釋為標識符或字符串字符的一部分。 控制字符即'、"、()等。

例如,<script>alert\u0028"xss"); </script>,(進行了 Unicode 編碼,那么解碼后它不再是作為控制字符,而是作為標識符的一部分alert(。

因此函數(shù)的括號之類的控制字符進行Unicode 轉(zhuǎn)義后是不能被正常解釋的。

總結(jié):Unicode 序列不能出現(xiàn)在控制字符中,否則不能被解釋。

示例 1:

<script>\u0061\u006c\u0065\u0072\u0074\u0028\u0031\u0031\u0029</script>

被編碼部分為 alert(11)。

該例子中的 JS 不會被執(zhí)行,因為控制字符被編碼了。

示例 2:

<script>\u0061\u006c\u0065\u0072\u0074(\u0031\u0032)</script>

被編碼部分為 alert 及括號內(nèi)為12。

該例子中 JS 不會被執(zhí)行,原因在于括號內(nèi)被編碼的部分不能被正常解釋,即使反編碼之后為數(shù)字,但是仍然按照字符串來處理(這里的12為字符串12,并不是int整數(shù))。要么使用 ASCII 數(shù)字,要么加""或''使其變?yōu)樽址?,作為字符串也只能作為普通字符?/p>

示例 3:

<script>alert('13\u0027)</script>

被編碼處為'。

該例的 JS 不會執(zhí)行,因為控制字符被編碼了,解碼后的'將變?yōu)樽址囊徊糠?,而不再解釋為控制字符。因此該例中字符串是不完整的,因為沒有'來結(jié)束字符串。

示例 4:

<script>alert('14\u000a')</script>

該例的 JS 會被執(zhí)行,因為被編碼的部分處于字符串內(nèi),只會被解釋為普通字符,不會突破字符串上下文。

示例 5:

<img src="1" onerror=\u0061\u006c\u0065\u0072\u0074\u0028\u0031\u0029>

此例無法執(zhí)行。我們以瀏覽器的視角來看:首先讀到<開始讀取標簽,然后讀到 onerror 調(diào)用 JS 解析器。 在JS中,單引號,雙引號和圓括號等屬于控制字符,編碼后將無法識別。所以對于防御來說,應該編碼這些控制字符。

下面這種方式可以解析:

<img src="1" onerror=\u0061\u006c\u0065\u0072\u0074('\u0031')>

可以結(jié)合上面的 HTML 編碼 按照解析順序反過去,先 JS 編碼然后 HTML 解碼:

<img src="1" onerror=&amp#92;&amp#117;&amp#48;&amp#48;&amp#54;&amp#49;&amp#92;&amp#117;&amp#48;&amp#48;&amp#54;&amp#99;&amp#92;&amp#117;&amp#48;&amp#48;&amp#54;&amp#53;&amp#92;&amp#117;&amp#48;&amp#48;&amp#55;&amp#50;&amp#92;&amp#117;&amp#48;&amp#48;&amp#55;&amp#52;&amp#40;&amp#39;&amp#92;&amp#117;&amp#48;&amp#48;&amp#51;&amp#49;&amp#39;&amp#41;>

瀏覽器讀到了<標簽開始構(gòu)造語法樹,然后 HTML 解碼,解碼之后發(fā)現(xiàn) onerror 于是進行 一個 JS 解碼,成功彈窗

延伸:

開發(fā)人員單純的設置 HTML 實體編碼為防御 xss 的手段,但是用戶輸入點在 alert 中 <img src = "https://text.com" onclick = 'alert("輸入點")'>

如果用戶正常輸入的話凡是存在< ," 等都能被轉(zhuǎn)碼

攻擊者可以通過語句 ");alert("test,在服務端被轉(zhuǎn)碼:

<img src ="https://gss1.bdstatic.com" onclick = 'alert("FIRSTXSS&amp#34;&amp#41;&amp#59;&amp#97;&amp#108;&amp#101;&amp#114;&amp#116;&amp#40;&amp#34;&amp#116;&amp#101;&amp#115;&amp#116;")'>

彈窗兩次,是因為瀏覽器進行 HTML 解碼發(fā)現(xiàn)存在兩個 alert()

所以對于這種情況,正確防御 XSS 的方法:

應該是先 JavaScript 編碼然后再進行 HTML 編碼用戶輸入 ");alert("test 后在服務端先 JavaScript 編碼然后再進行 HTML 編碼到瀏覽器端:首先經(jīng)過第一步 HTML 解碼后變?yōu)閈u0022\u0029\u003B\u0061\u006C\u0065\u0072\u0074\u0028\u0022\u0074\u0065\u0073\u0074。JavaScript 解析器工作,變?yōu)?quot;);alert("test ,剛才已經(jīng)講過 JavaScript 解析時只有標識符名稱不會被當做字符串,控制字符僅會被解析為標示符名稱或者字符串,因此 \u0022 被解釋成雙引號文本,\u0028 和\u0029 被解釋成為圓括號文本,不會變?yōu)榭刂?字符被解析執(zhí)行。

在這里采用的先 JS 編碼后 HTML 編碼中只彈窗了一次。

3.3 URL 解析器

通用 URI 的格式如下:

[協(xié)議名]://[用戶名]:[密碼]@[主機名]:[端口]/[路徑]?[查詢參數(shù)]#[片段 ID]

如何對瀏覽器解析和XSS的深度探究

URL 解析器也被建模為狀態(tài)機,文檔輸入流中的字符可以將其導向不同的狀態(tài)。

首先,要注意的是 URL 的 Scheme 部分(協(xié)議部分)必須為 ASCII 字符,即不能被任何編碼,否則 URL 解析器的狀態(tài)機將進入 No Scheme 狀態(tài)。

示例1:

<ahref="%6a%61%76%61%73%63%72%69%70%74:%61%6c%65%72%74%28%31%29"></a>

URL 編碼部分的是javascript:alert(1)。

JS 不會被執(zhí)行,因為作為Scheme 部分的"javascript"這個字符串被編碼(其中javascript是一種偽協(xié)議),導致 URL 解析 器狀態(tài)機進入 No Scheme 狀態(tài)。

URL 中的:也不能被以任何方式編碼,否則 URL 解析器的狀態(tài)機也將進入 No Scheme 狀態(tài)。

<ahref="javascript%3aalert(3)"></a>

由于:被 URL 編碼為%3a,導致 URL 狀態(tài)機進入 NoScheme 狀態(tài),JS 代碼不能執(zhí)行。

示例2:

<ahref="&amp#x6a;&amp#x61;&amp#x76;&amp#x61;&amp#x73;&amp#x63;&amp#x72;&amp#x69;&amp#x70;&amp#x74;:%61%6c%65%72%74%28%32%29">

"javascript"這個字符串被實體化編碼,:沒有被編碼,alert(2)被 URL編碼。 成功執(zhí)行 首先,在 HTML 解析器中我們談到過,HTML狀態(tài)機處于屬性值狀態(tài)(Attribute Value State)時,字符實體時會被解碼的,此處在 href 屬性中,所以被實體化編碼的 "javascript"字符串會被解碼。其次,HTML 解析是在URL解析之前的,所以在進行 URL 解析之前,Scheme 部分的"javascript"字符串已被解碼,而并不再是被實體編碼的狀態(tài)。

四、解析順序

首先瀏覽器接收到一個 HTML 文檔時,會觸發(fā) HTML 解析器對 HTML 文檔進行詞法解析,這一過程完成 HTML 解碼并創(chuàng)建 DOM 樹。

接下來 JavaScript 解析器會介入對內(nèi)聯(lián)腳本進行解析,這一過程完成 JS 的解碼工作。

如果瀏覽器遇到需要 URL 的上下文環(huán)境,這時 URL 解析器也會介入完成 URL 的解碼工 作,URL 解析器的解碼順序會根據(jù) URL 所在位置不同,可能在 JavaScript 解析器之前或之后解析。

總之HTML 解析總是第一步。URL 解析和 JavaScript 解析,它們的解析順序要根據(jù)情況而定。

示例 1:

<a href="UserInput"></a>

該例子中,首先由 HTML 解析器對UserInput 部分進行字符實體解碼;

接著 URL 解析器對UserInput 進行 URL decode;

如果 URL 的 Scheme 部分為 javascript 的話,JavaScript 解析器會再對 UserInput 進行解 碼。

所以解析順序是:HTML 解析->URL解析->JavaScript 解析。

示例 2:

<a href=# onclick="window.open('UserInput')"></a>

該例子中,首先由 HTML 解析器對UserInput 部分進行字符實體解碼;

接著由 JavaScript 解析器會再對 onclick 部分的 JS 進行解析并執(zhí)行 JS;

執(zhí)行 JS 后window.open('UserInput')函數(shù)的參數(shù)會傳入 URL,所以再由 URL 解析器對 UserInput 部分進行解碼。

因此解析順序為:HTML 解析->JavaScript解析->URL 解析。

示例 3:

<a href="javascript:window.open('UserInput')">

該例子中,首先還是由 HTML 解析器對 UserInput 部分進行字符實體解碼;

接著由 URL 解析器解析 href 的屬性值;

然后由于Scheme為javascript,所以由 JavaScript 解析;

解析執(zhí)行 JS 后window.open('UserInput')函數(shù)傳入 URL,所以再由 URL 解析器解析。

所以解析順序為:HTML 解析->URL解析->JavaScript 解析->URL 解析。

綜合實例:

<a href="&amp#x6a;&amp#x61;&amp#x76;&amp#x61;&amp#x73;&amp#x63;&amp#x72;&amp#x69;&amp#x70;&amp#x74;&amp#x3a;&amp#x25;&amp#x35;&amp#x63;&amp#x25;&amp#x37;&amp#x35;&amp#x25;&amp#x33;&amp#x30;&amp#x25;&amp#x33;&amp#x30;&amp#x25;&amp#x33;&amp#x36;&amp#x25;&amp#x33;&amp#x31;&amp#x25;&amp#x35;&amp#x63;&amp#x25;&amp#x37;&amp#x35;&amp#x25;&amp#x33;&amp#x30;&amp#x25;&amp#x33;&amp#x30;&amp#x25;&amp#x33;&amp#x36;&amp#x25;&amp#x36;&amp#x33;&amp#x25;&amp#x35;&amp#x63;&amp#x25;&amp#x37;&amp#x35;&amp#x25;&amp#x33;&amp#x30;&amp#x25;&amp#x33;&amp#x30;&amp#x25;&amp#x33;&amp#x36;&amp#x25;&amp#x33;&amp#x35;&amp#x25;&amp#x35;&amp#x63;&amp#x25;&amp#x37;&amp#x35;&amp#x25;&amp#x33;&amp#x30;&amp#x25;&amp#x33;&amp#x30;&amp#x25;&amp#x33;&amp#x37;&amp#x25;&amp#x33;&amp#x32;&amp#x25;&amp#x35;&amp#x63;&amp#x25;&amp#x37;&amp#x35;&amp#x25;&amp#x33;&amp#x30;&amp#x25;&amp#x33;&amp#x30;&amp#x25;&amp#x33;&amp#x37;&amp#x25;&amp#x33;&amp#x34;&amp#x28;&amp#x31;&amp#x35;&amp#x29;"></a>

首先 HTML 解析器進行解析,解析到href 屬性的值時,狀態(tài)機進入屬性值狀態(tài)(Attribute Value State),該狀態(tài)會解碼字符實體;

接著由 URL 解析器進行解析并解碼;

再接著由于 Scheme 為javascript,因此由 JavaScript 解析器解析并解碼,加上編碼部分是函 數(shù)名,屬于標識符,因此可以正常解碼解釋;

經(jīng)過三輪解析解碼后得到結(jié)果:<a href="javascript:alert(15)"></a>

關于如何對瀏覽器解析和XSS的深度探究就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。

AI