作者:LoRexxar’@知道創宇404實驗室
英文版本:https://paper.seebug.org/939/
0x01 前端防禦的開始
對於一個基本的XSS漏洞頁面,它發生的原因往往是從用戶輸入的數據到輸出沒有有效的過濾,就比如下面的這個範例代碼。
<?php
$a = $_GET['a'];
echo $a;
對於這樣毫無過濾的頁面,我們可以使用各種方式來構造一個xss漏洞利用。
a=<script>alert(1)</script>
a=<img/src=1/onerror=alert(1)>
a=<svg/onload=alert(1)>
對於這樣的漏洞點來說,我們通常會使用htmlspecialchars函數來過濾輸入,這個函數會處理5種符號。
& (AND) => &
" (雙引號) => " (當ENT_NOQUOTES沒有設置的時候)
' (單引號) => ' (當ENT_QUOTES設置)
< (小於號) => <
> (大於號) => >
一般意義來說,對於上面的頁面來說,這樣的過濾可能已經足夠了,但是很多時候場景永遠比想象的更多。
<a href="{輸入點}">
<div style="{輸入點}">
<img src="{輸入點}">
<img src={輸入點}>(沒有引號)
<script>{輸入點}</script>
對於這樣的場景來說,上面的過濾已經沒有意義了,尤其輸入點在script標籤里的情況,剛才的防禦方式可以說是毫無意義。
一般來說,為了能夠應對這樣的xss點,我們會使用更多的過濾方式。
首先是肯定對於符號的過濾,為了能夠應對各種情況,我們可能需要過濾下面這麼多符號
% * + , – / ; < = > ^ | `
但事實上過度的過濾符號嚴重影響了用戶正常的輸入,這也是這種過濾使用非常少的原因。
大部分人都會選擇使用htmlspecialchars+黑名單的過濾方法
on/w+=
script
svg
iframe
link
…
這樣的過濾方式如果做的足夠好,看上去也沒什麼問題,但回憶一下我們曾見過的那麼多XSS漏洞,大多數漏洞的產生點,都是過濾函數忽略的地方。
那麼,是不是有一種更底層的防禦方式,可以從瀏覽器的層面來防禦漏洞呢?
CSP就這樣誕生了…
0x02 CSP(Content Security Policy)
Content Security Policy (CSP)內容安全策略,是一個附加的安全層,有助於檢測並緩解某些類型的攻擊,包括跨站腳本(XSS)和數據注入攻擊。
CSP的特點就是他是在瀏覽器層面做的防護,是和同源策略同一級別,除非瀏覽器本身出現漏洞,否則不可能從機制上繞過。
CSP只允許被認可的JS塊、JS文件、CSS等解析,只允許向指定的域發起請求。
一個簡單的CSP規則可能就是下面這樣
header("Content-Security-Policy: default-src 'self'; script-src 'self' https://lorexxar.cn;");
其中的規則指令分很多種,每種指令都分管瀏覽器中請求的一部分。
Google團隊利用他們強大的搜索引擎庫,分析了超過160w台主機的CSP部署方式,他們發現。
加載腳本最常列入白名單的有15個域,其中有14個不安全的站點,因此有75.81%的策略因為使用了了腳本白名單,允許了攻擊者繞過了CSP。總而言之,我們發現嘗試限制腳本執行的策略中有94.68%是無效的,並且99.34%具有CSP的主機制定的CSP策略對xss防禦沒有任何幫助。
在paper中,Google團隊正式提出了兩種以前被提出的CSP種類。
1、nonce script CSP
header("Content-Security-Policy: default-src 'self'; script-src 'nonce-{random-str}' ");
動態的生成nonce字符串,只有包含nonce字段並字符串相等的script塊可以被執行。
<script nonce="{random-str}">alert(1)</script>
這個字符串可以在後端實現,每次請求都重新生成,這樣就可以無視哪個域是可信的,只要保證所加載的任何資源都是可信的就可以了。
<?php
Header("Content-Security-Policy: script-src 'nonce-".$random." '"");
?>
<script nonce="<?php echo $random?>">
2、strict-dynamic
header("Content-Security-Policy: default-src 'self'; script-src 'strict-dynamic' ");
SD意味着可信js生成的js代碼是可信的。
這個CSP規則主要是用來適應各種各樣的現代前端框架,通過這個規則,可以大幅度避免因為適應框架而變得鬆散的CSP規則。
Google團隊提出的這兩種辦法,希望通過這兩種辦法來適應各種因為前端發展而出現的CSP問題。
但攻與防的變遷永遠是交替升級的。
1、nonce script CSP Bypass
2016年12月,在Google團隊提出nonce script CSP可以作為新的CSP趨勢之後,聖誕節Sebastian Lekies提出了nonce CSP的致命缺陷。
Nonce CSP對純靜態的dom xss簡直沒有防範能力
http://sirdarckcat.blogspot.jp/2016/12/how-to-bypass-csp-nonces-with-dom-xss.html
Web2.0時代的到來讓前後台交互的情況越來越多,為了應對這種情況,現代瀏覽器都有緩存機制,但頁面中沒有修改或者不需要再次請求後台的時候,瀏覽器就會從緩存中讀取頁面內容。
從location.hash就是一個典型的例子
如果JS中存在操作location.hash導致的xss,那麼這樣的攻擊請求不會經過後台,那麼nonce后的隨機值就不會刷新。
這樣的CSP Bypass方式我曾經出過ctf題目,詳情可以看
https://lorexxar.cn/2017/05/16/nonce-bypass-script/
除了最常見的location.hash,作者還提出了一個新的攻擊方式,通過CSS選擇器來讀取頁面內容。
*[attribute^="a"]{background:url("record?match=a")}
*[attribute^="b"]{background:url("record?match=b")}
*[attribute^="c"]{background:url("record?match=c")} [...]
當匹配到對應的屬性,頁面就會發出相應的請求。
頁面只變化了CSS,純靜態的xss。
CSP無效。
2、strict-dynamic Bypass
2017年7月 Blackhat,Google團隊提出了全新的攻擊方式Script Gadgets。
header("Content-Security-Policy: default-src 'self'; script-src 'strict-dynamic' ");
Strict-dynamic的提出正是為了適應現代框架
但Script Gadgets正是現代框架的特性
转载请注明:IAMCOOL » 前端防禦從入門到棄坑–CSP變遷