前端安全的話題再次被說起,大到深航東航系統被攻破,乘客信息泄露並被利用,小到一個word-wrap讓服務器暴露信息,各類漏洞和攻擊無所不在。在前端江湖中,攻擊與防守更像是一場漫無硝煙的戰爭,悄無聲息卻無時無刻都在進行。javascript
前端常見漏洞包括XSS、CSRF及界面操做劫持,服務端如SQL注入等。css
前端使用的傳輸協議是http,不通過加密直接傳輸的。HTML中有不少地方能夠內嵌腳本。及前端的存儲,cookie,web storage.這些在必定程度上帶來了一些安全風險。html
同域:兩個站點同協議,同域名,同端口。前端
出於防範跨站腳本攻擊的同源安全策略,瀏覽器禁止客戶端腳本(如Javascript)對不一樣域名的服務進行跨域調用。java
同源策略(Same Origin)中的源有着嚴格的定義,參見RFC6454,第4章節。通常而言,Origin由{protocol, host, port}三部分組成。web
下面是同源檢查的一些實例express
可能有點意外的是,通常咱們會認爲不一樣的子域名應該被當作同域名,是安全的調用,但實際上瀏覽器同源策略甚至禁止了不一樣子域名和端口的服務之間的調用。跨域
跨子域的訪問,可以使用iframe,而後設置domain爲相同。瀏覽器
HTTP相關頭安全
User-Agent:在使用HTTP協議進行請求時,HTTP協議頭部會添加User-Agent,該信息能夠標識請求者的一些信息,如什麼瀏覽器類型和版本、操做系統,使用語言等信息。
Referer:HTTP Referer是header的一部分,當瀏覽器向web服務器發送請求的時候,通常會帶上Referer,告訴服務器我是從哪一個頁面連接過來的,服務器籍此能夠得到一些信息用於處理。好比從我主頁上連接到一個朋友那裏,他的服務器就可以從HTTP Referer中統計出天天有多少用戶點擊我主頁上的連接訪問他的網站。HTTP Referer使用很是簡單,使用場合比較多的是用於頁面統計、資源防盜鏈等,但仍是有一點值得注意的是:Referer是不安全的,客戶端能夠經過設置改變 Request中的值,儘可能不要用來進行安全驗證等方面。
HTML中內嵌腳本執行
可內嵌於js文件中,可出如今HTML的script標籤中,HTML標籤的事件中,及一些標籤的src,href等屬性的僞協議javascript:中。這樣致使防護XSS有些棘手。使用HTTPOnly cookie能夠必定程度防止腳本獲取cookie。
AJAX
AJAX的請求頭設置是有限制的。AJAX嚴格遵照同源策略。
XSS攻擊
XSS是一種常常出如今web應用中的安全漏洞,它容許惡意web用戶將代碼植入到提供給其它用戶使用的頁面中。好比這些代碼包括HTML代碼和客戶端腳本。攻擊者利用XSS漏洞旁路掉訪問控制——例如同源策略(same origin policy)。首先,攻擊者向web頁面注入惡意代碼,而後,這段代碼被瀏覽器執行,這樣,一次攻擊就成功了。
分類
1.XSS反射型攻擊,惡意代碼並無保存在目標網站,經過引誘用戶點擊一個連接到目標網站的惡意連接來實施攻擊的。
2.XSS存儲型攻擊,惡意代碼被保存到目標網站的服務器中,這種攻擊具備較強的穩定性和持久性,比較常見場景是在博客,論壇等社交網站上,但OA系統,和CRM系統上也能看到它身影,好比:某CRM系統的客戶投訴功能上存在XSS存儲型漏洞,黑客提交了惡意攻擊代碼,當系統管理員查看投訴信息時惡意代碼執行,竊取了客戶的資料,然而管理員絕不知情,這就是典型的XSS存儲型攻擊。
一個簡單的例子,在發帖子的時候,嵌入
<script>alert('XSS')</script>
若是在帖子內容裏出現了這樣的語句,被瀏覽器執行了,一次XSS攻擊就成功了。 要避免此類攻擊,通常會對用戶輸入採起過濾,最多見的就是對<>轉換成<以及>,通過轉換之後<>雖然可在正確顯示在頁面上,可是已經不能構成代碼語句了。這個貌似很完全,由於一旦<>被轉換掉,什麼<script src=1.js></script>就會轉換成「<script src=1.js></script>」,不能執行,所以,不少人認爲只要用戶的輸入沒有構成<>,就不能閉合先後的標籤,其語句固然也不會有害,可是,萬事總有可能,只要有必定的條件,咱們就能夠構造通過編碼後的語句來進行XSS。
再好比
<img src='javascript:alert("XSS")'>
這類攻擊須要過濾用戶輸入的src屬性,過濾掉裏邊的javascript開頭的關鍵字,封堵XSS。另外對於各類可能繞過過濾的狀況,封堵XSS漏洞。
不少時候,產生XSS的地方會有變量的長度限制,這個限制多是服務器端邏輯形成的。假設下面代碼存在一個XSS漏洞:
<input type=text value="$var" />
服務器端對輸出變量$var作了嚴格的長度限制,那麼攻擊者可能會這樣構造XSS:
但願達到的輸出效果是:
<input type=text value=""><script>alert(/xss/)</script>" />
假設長度限制爲20個字節,則這段XSS會被切割爲:
$var輸出爲: "><script>alert(/xss
連一個完整的函數都沒法寫完。攻擊者能夠利用事件(Event)來縮短所須要的字節數:
$var輸出爲 "onclick=alert(1)//加上空格符,恰好夠20個字節
但利用「事件」可以縮短的字節數是有限的。最好的辦法是先把XSS Payload寫在別處,再經過簡短的代碼加載這段XSS Payload。
最經常使用的一個「藏代碼」的地方,就是「location.hash"。並且根據HTTP協議,location.hash的內容不會在HTTP包中發送,因此服務器端的Web日誌中並不會記錄下location.hash裏的內容,從而也更好地隱藏了黑客真實的意圖。
$var 輸出爲 " onclick="eval(location.hash.substr(1))
總共40個字節。輸出後的HTML是:
<input type=text value="" onclick="eval(location.hash.substr(1)) " />
由於location.hash的第一個字符是#,因此必須去除第一個字符才行。此時構造出的XSS URL爲:
http://www.a.com/test.html#alert(1)
用戶點擊文本框時,location.hash裏的代碼就執行了.
location.hash自己沒有長度限制,可是瀏覽器的地址欄是有長度限制的,不過這個長度已經足夠寫很長的XSS Payload了。要是地址欄的長度也不夠用,還能夠再使用加載遠程JS的方法,來寫更多的代碼。
在某些環境下,能夠利用註釋符繞過長度限制。
好比咱們能控制兩個文本框,第二個文本框容許寫入更多的字節。此時能夠利用HTML的」註釋符號「,把兩個文本框之間的HTML代碼全註釋掉,從而」打通「兩個<input>標籤。
<input id=1 type="text" value="" />
xxxxx
<input id=2 type="text" value="" />
在第一個input框中,輸入:"><!--
在第二個input框中,輸入:--><script>alert(/xss/);</script>
最終的效果是:
<input id=1 type="text" value=""><!-- />
xxxxx
<input id=2 type="text" value="--><script>alert(/xss/);</script>" />
中間的代碼前部被<!-- -->註釋掉了。
XSS的防護:
1)HttpOnly:瀏覽器將禁止頁面的Javascript訪問帶有HttpOnly屬性的Cookie。是爲了解決劫持Cookie攻擊。由於Javascript獲取不到Cookie的值。
C#中設置HttpOnly的方法:
HttpCookie
myCookie=new HttpCookie("myCookie");
myCookie.HttpOnly=true;
Response.AppendCookie(myCookie);
2)輸入檢查:
常見的Web漏洞如XSS、SQL諸如等,都要求攻擊者構造一些特殊字符,這些特殊字符多是正經常使用戶不會用到的,因此輸入檢查就有存在的必要了。
例如,用戶名可能會被要求只能爲字母、數字的組合。
輸入檢查的邏輯應該放在服務器端,不然很容易被繞過。目前的廣泛作法是在客戶端和服務器端都執行檢查。
在XSS的防護上,輸入檢查通常是檢查用戶輸入的數據中是否包含一些特殊字符,如< > ' "等。若是發現,則將這些字符過濾掉或編碼。
比較智能的還會檢查<script> javascript等敏感字符,網上有不少XSS Filter資源。但由於XSS Filter對語境不瞭解,有時不能檢查出XSS攻擊。
3)輸出檢查:
通常來講,除了富文本的輸出外,在變量輸出到HTML頁面時,可使用編碼或者轉移的方式來防護XSS攻擊。
1)安全的編碼函數:
針對HTML代碼的編碼方式是HtmlEncode。
HtmlEncode並不是專用名詞,它只是一種函數體現。它的做用是將字符轉換成HTMLEntities,對應的標準是ISO-8859-1。
對了對抗XSS,在HtmlEncode中至少要轉換如下字符:& => & < =>< > " ' /
相應地,Javascript的編碼方式可使用JavascriptEncode。
JavascriptEncode與HtmlEncode的編碼方法不一樣,他須要使用\對特殊字符進行轉義。在對抗CSS時,還要求輸出的變量必須在引號內部,以免形成安全問題。比較下面兩種寫法:
var x=escapeJavascript($evil);
var y='"'+escapeJavascript($evil)+'"';
若是escapeJavascript函數只轉義了幾個危險字符,好比' " < > \ & #等,那麼上面的兩行代碼輸出後可能變成:
var x=1;alert(2);
var y="1;alert(2)";
因此要求使用JavascriptEncode的變量輸出必定要在引號內。
可視不少開發者沒有這個習慣怎麼辦?這將只能使用一個更加嚴格的JavascriptEncode函數--除了數字、字母外的全部字符,都使用十六進制"\xHH"的方式進行編碼。在本例中:
var x=1;alert(2);
變成了:
var x=1\x3balert\x282\x29;
如此代碼能夠保證是安全的。
2)只需一種編碼嗎?
XSS攻擊主要發生在MVC架構中的View層。大部分的XSS漏洞能夠在模版系統中解決。
不是使用了auto-escape就萬事大吉了,XSS的防護須要區分狀況對待。
4)正確地防護XSS
XSS的本質仍是一種"HTML注入」。
想要根治XSS,能夠列出全部XSS可能發生的場景,在一一解決。
a)在HTML標籤中輸出:如
<div>$var</div>
<a href=# >$var</a>
這樣能夠構造出:
<div><script>alert(/xss/)</script></div><a href=#><img scr=# onerror=alert(1) /></a>
防護方法是對變量使用HtmlEncode。
b)在HTML屬性中輸出:如
<div id="abc" name="$var" ></div>
能夠構造出:
<div id="abc" name=""><script>alert(/xss/)</script><"" ></div>
防護方法也是HtmlEncode。在OWASP ESAPI中推薦了一種更嚴格的HtmlEncode--除了字母、數字外,其餘全部的字符都被編碼成HTMLEntities。
String sfa=ESAPI.encoder().encodeForHTMLAttribute(request.getParameter("input")];
這種嚴格的編碼方式,能夠保證不會出現任何安全問題。
c)在<script>標籤中輸出:如
<script>
var x="$var";
</script>
能夠構造出:
<script>
var x="";alert(/xss/);//";
</script>
防護時也是使用JavascriptEncode
d)在事件中輸出:如
<a href=# onclick="funcA('$var')" >test</a>
能夠構造出:
<a href=# onclick="funcA('');alert(/xss/);//')" >test</a>
在防護時須要使用JavascriptEncode
e)在CSS中輸出:在CSS和style、style attribute中造成的XSS的方式很是多樣化,參考下面幾個XSS的例子。
<STYLE>@import 'http://ha.ckers.org/xss.css';</STYLE>
<STYLE>BODY
{-moz-binding:url("http://ha.ckers.org/xssmoz.xml#xss")}</STYLE>
<XSS STYLE="behavior:url(xss.htc);">
<STYLE>li
{list-style-image:url("javascript:alert('XSS')");}
</STYLE><UIL><LI>XSS
<DIV
STYLE="background-image:url(javascript:alert('XSS'))">
<DIV
STYLE="width:expression(alert('XSS'));">
因此通常來講,儘量地禁止用戶可控制的變量在"<style>標籤"、"HTML標籤的style屬性"以及"CSS文件"中輸出。若是必定有這樣的需求,推薦使用OWASP ESAPI中的encodeForCSS()函數。相似於ESAPI.encoder().encodeForJavaScript()函數。
f)在地址中輸出:
在地址中輸出也較爲複雜。通常來講,在URL的path(路徑)或者search(參數)中輸出,使用URLEncode便可。
例如:
<a href="http://www.evil.com/?test=$var" >test</a>
可能的攻擊辦法:
<a href="http://www.evil.com/?test=" onclick=alert(1)"" >test</a>
通過URLEncode後就能夠防護。
可是還有一種狀況,就是整個URL都可以被用戶徹底控制。這時URL的協議和Host部分是不可以使用URLEncode的,不然會改變URL的語義。
一個URL的組成以下:
[Protocol][host][Path][search][Hash]
如
http://www.evil.com/a/b/c/test?abc=123#ssss
protocol="http://"host=www.evil.comsearch="?abc=123"
hash=#ssss
在Protocol和host中,若是使用嚴格的URLEncode函數,則會把"://"、"."等都編碼掉。
對於以下的輸出方式:
<a href="http://supershll.blog.163.com/blog/$var" >test</a>
攻擊者可能會構造:
<a href="javascript:alert(1);">test</a>
除了「JavaScript做爲僞協議能夠執行代碼外,還有VBScript、dataURI等僞協議可能致使腳本執行。
因而可知,若是用戶可以徹底控制URL,則能夠執行腳本的方式有不少,如何解決這種狀況呢?
通常來講,若是變量是整個URl,則應該首先檢查變量是不是以Http開頭,若是不是則自動添加,以保證不會出現僞協議類的XSS攻擊。
在此以後再對變量進行URLEncode,便可保證不會有此類的XSS發生了。
OWASP
ESAPI中有一個URLEncode的實現:
ESAPI.encoder().encodeForURL
5)處理富文本:
有些時候,網站須要容許用戶提交一些自定義的HTML代碼,稱之爲"富文本"。好比,一個用戶在論壇裏發帖,帖子裏的內容裏要有圖片、視頻、表格等,這些「富文本」的效果都須要經過HTML代碼來實現。
如何區分安全的「富文本"和有攻擊性的XSS呢?
在處理富文本時,仍是要回到"輸入檢查"的思路上來。」輸入檢查「的主要功能是,在檢查時還不知道變量的輸出語境。但用戶提交的」富文本「數據,其語義是完整的HTML代碼,在輸出時也不會拼湊到某個標籤的屬性中。所以能夠特殊狀況特殊處理。
在上面列出了全部在HTML中可能執行腳本的地方。而一個優秀的」XSS Filter「,也應該可以找出HTML代碼中全部可能執行腳本的地方。
HTML是一種結構化的語言,比較好分析。經過htmlparser能夠解析出HTML代碼的標籤、標籤屬性和事件。
在過濾富文本時,」事件「應該被嚴格禁止,由於」富文本「的展現需求裏不該該包括」事件「這種動態效果。而一些危險的標籤,好比<iframe>、<script>、<base>、<form>等,也是應該嚴格禁止的。
在標籤的選擇上,應該使用白名單,避免使用黑名單。好比,只容許<a>、<img>、<div>等比較」安全「的標籤存在。
」白名單「原則不只僅用於標籤的選擇,一樣應該用於屬性與事件的選擇。
在富文本過濾中,處理CSS也是一件麻煩的事情。若是容許用戶自定義的CSS、style,則也可能致使XSS攻擊。所以儘量地禁止用戶自定義CSS與Style。
若是必定要容許用戶自定義樣式,則只能像過濾」富文本「同樣過濾」CSS「。這須要一個CSS Parser對樣式進行智能分析,檢查其中是否包含危險代碼。
6)防護DOM Based XSS:
DOM Based XSS是一種比較特別的XSS漏洞,前文提到的幾種防護方法都不太適用,須要特別對待。
DOM Based XSS是如何造成的呢?回頭看看這個例子:
<script>
function test(){
var str=document.getElementById("text").value;
document.getElementById("t").innerHTML="<a href='http://supershll.blog.163.com/blog/"+str+"' >testLink</a>";
}
</script>
<div id="t"></div>
<input type="text" id="text" value="" />
<input type="button" id="s" value="write" oncick="test()" />
在button的onclick事件中,執行了test()函數,而該函數中最關鍵的一句是:
document.getElementById("t").innerHTML="<a href='http://supershll.blog.163.com/blog/"+str+"' >testLink</a>";
將HTML代碼寫入了DOM節點,最後致使了XSS的發生。
事實上,DOM Based XSS是從Javascript中輸出數據到HTML頁面中。而前文提到的方法都是針對"從服務器應用直接輸出到HTML頁面」的XSS漏洞,所以並不適用於DOM
Based XSS。
看看下面這個例子:
<script>
var x="$var";
document.write("<a href='http://supershll.blog.163.com/blog/"+x+"' >test</a>");
</script>
變量"$var"輸出在<script>標籤內,但是最後又被document.write輸出到HTML頁面中。
假設爲了保護"$var"直接在<script>標籤內產生XSS,服務器端對齊進行了JavascriptEscape。但是$var在document.write時,仍然可以產生XSS,以下所示:
<script>
var x="\x20\x27onlick\x3dalert\x281\x29\x3b...";
document.write("<a href='http://supershll.blog.163.com/blog/」+x+"' >test</a>");
</script>
頁面渲染以後的實際結果以下:
XSS攻擊成功。
其緣由在於,第一次執行JavascriptEscape後,只保護了:
var x="$var";
可是當document.write輸出數據到HTML頁面時,瀏覽器從新渲染了頁面。在<script>標籤執行時,已經對變量x進行了解碼,其後document.write再運行時,其參數就變成了:
<a href='http://supershll.blog.163.com/blog/ 'onclick=alert(1);//' ' >test</a>
XSS所以而產生。
若是改爲HtmlEncode也是如此。
正確的防護方法是什麼呢?
首先,在"$var"輸出到<script>時,應該執行一次javascriptEncode;其次,在document.write輸出到HTML頁面時,要分具體狀況看待:若是是輸出到事件或者腳本,則要再作一次javascriptEncode;若是是輸出到HTML內容或者屬性,則要作一次HtmlEncode。
也就是說,從JavaScript輸出到HTML頁面,也至關於一次XSS輸出的過程,須要分語境使用不一樣的編碼函數。
會觸發DOM Based XSS的地方不少,如下幾個地方是JavaScript輸出到HTML頁面的必經之路。
a) document.write() document.writeln()
b) xxx.innerHTML= xxx.outerHTML=
c) innerHTML.replace
d) document.attachEvent() window.attachEvent()
e) document.location.replace() document.location.assign()...
除了服務器端直接輸出變量到Javascript以外,還有如下幾個地方可能會成爲DOM Based XSS的輸入點,也須要重點關注。
a) 頁面中全部的inputs框
b) window.location(href、hash等)
c) window.name
d) document.referrer
e) document.cookie
f) localstorage
g) XMLHttpRequest返回的數據
7)
換個角度看XSS的風險
從業務角度看XSS風險,用戶之間有互動的頁面的風險確定比沒有互動的頁面的風險高。應重點修補風險高的頁面。
理論上,XSS漏洞雖然複雜,但倒是能夠完全解決的。在設計XSS解決方案時,應該深刻理解XSS攻擊的原理,針對不一樣的場景使用不一樣的方法。
CSRF攻擊
CSRF是Cross Site Request Forgery的縮寫(也縮寫爲XSRF),直譯過來就是跨站請求僞造的意思,也就是在用戶會話下對某個CGI作一些GET/POST的事情——這些事情用
戶未必知道和願意作,你能夠把它想作HTTP會話劫持。
防護:使用驗證碼,加密等方法防護。請求Referer驗證。cookie設置爲HTTPOnly,防止被竊取。