web安全防範之XSS漏洞***

 

web 安全防範之XSS漏洞***
最近在 cnode社區,由@吳中驊的一篇關於XSS的文章,直接致使了社區的人開始在cnode嘗試各類***。本文總結了一下此次碰到的一些問題與解決方案。

 
文件上傳漏洞
以前 nodeclub在上傳圖片的時候邏輯是這樣的:
1. //用戶上傳的文件名 
2. var filename = Date.now() + '_' + file.name; 
3. //用戶文件夾 
4. var userDir = path.join(config.upload_dir, uid); 
5. //最終文件保存的路徑 
6. var savepath = path.join(userDir, filename); 
7. //將用戶上傳的文件從臨時目錄移動到最終保存路徑 
8. fs.rename(file.path, savepath, callback); 
9.  
看上去好像沒有問題,每一個人上傳的文件都存放在以用戶 UID命名的一個文件夾內,而且以當前的時間戳做前綴。可是當有用戶惡意構造輸入的時候,問題就出現了。當用戶上傳的文件filename爲/../../xxx的時候,上傳的文件就會rename到用戶文件夾以外,致使用戶能夠替換現有系統上的任何文件。
這個漏洞相對來講很是的低級,可是後果倒是最嚴重的,直接致使整個系統均可能被用戶控制。修復的方法也很簡單:
1. var filename = Date.now() + '_' + file.name; 
2.  
3. var userDir = path.join(config.upload_dir, uid); 
4.  
5. //獲取最終保存到的絕對路徑 
6. var savepath = path.resolve(path.join(userDir, filename)); 
7. //驗證 
8. if (savepath.indexOf(path.resolve(userDir)) !== 0) { 
9. return res.send({status: 'forbidden'}); 
10.
11.fs.rename(file.path, savepath, callback); 
12. 
富文本編輯器的XSS
關於 XSS,在@吳中驊的文章中已經很是詳細的描述了。而cnode社區中,用戶發表話題和回覆話題也是用的一個支持markdown格式的富文本編輯器。以前是沒有作過任何XSS防範措施的,因而...你能夠直接在裏面寫:
1.  <script>alert(123); </script>
2.  <div onmouseover="alert(123)" ></div>
3.  <a href="alert(123);" >123 </a>
4.  
markdown格式的內容也沒有作URL有效性檢測,因而各類樣式的XSS又出來了:
[xss][1]
[xss][2]
![xss][3]
[1]: alert(123);< /div>
[2]: http://www.baidu.com/#"onclick='alert(123)'
[3]: http://www.baidu.com/img.jpg#"onmouseover='alert(123)'
在社區這個應用場景下,引入 HTML標籤只是爲了進行一些排版的操做,而其餘的樣式定義等等都只會讓整個界面一團糟,更別說還有潛在的XSS漏洞風險。所以,其實咱們是不須要支持用戶輸入HTML標籤來進行內容排版的,一切均可以經過markdown來代替。而後經過簡單粗暴的HTML escape,就能夠消滅掉直接輸入HTML致使的XSS風險。
1. function escape(html) { 
2. return html.replace(/&(?!\w+;)/g, '&amp;') 
3. .replace(/ </g, ' &lt;') 
4. .replace( />/g, ' &gt;') 
5. .replace(/"/g, '&quot;'); 
6. }
然而這樣粗暴的進行 escape,會致使用戶輸入的代碼裏面的< > ;這些特殊字符也被轉義掉,不能正確顯示,須要先將代碼段提取出來保存,只轉義非代碼段的部分。因而這個escape函數變成了這樣:
1. function escape(html) { 
2. var codeSpan = /(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm; 
3. var codeBlock = /(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g; 
4. var spans = []; 
5. var blocks = []; 
6. var text = String(html).replace(/\r\n/g, '\n') 
7. .replace('/\r/g', '\n'); 
8. text = '\n\n' + text + '\n\n'; 
9. texttext = text.replace(codeSpan, function(code) { 
10.spans.push(code); 
11.return '`span`'; 
12.}); 
13.text += '~0'; 
14.return text.replace(codeBlock, function (whole, code, nextChar) { 
15.blocks.push(code); 
16.return '\n\tblock' + nextChar; 
17.}) 
18..replace(/&(?!\w+;)/g, '&amp;') 
19..replace(/ </g, ' &lt;') 
20..replace( />/g, ' &gt;') 
21..replace(/"/g, '&quot;') 
22..replace(/`span`/g, function() { 
23.return spans.shift(); 
24.}) 
25..replace(/\n\tblock/g, function() { 
26.return blocks.shift(); 
27.}) 
28..replace(/~0$/,'') 
29..replace(/^\n\n/, '') 
30..replace(/\n\n$/, ''); 
31.}; 
32. 
而對於 markdown生成的<a>標籤和<img>標籤中的href屬性,必需要作URL有效性檢測或者作xss的過濾。這樣保證經過markdown生成的HTML代碼也是沒有XSS漏洞的。
由於 XSS的手段確實比較多,見XSS Filter Evasion Cheat Sheet。所以可以作粗暴的HTML escape是最安全的,可是並非每個地方均可以經過markdown來代替HTML代碼,因此不是每個地方都能用HTML escape,這個時候就須要其餘的手段來過濾XSS漏洞了。
XSS防範只能經過定義白名單的形式,例如只容許 <p> <div> <a>標籤,只容許href class style屬性。而後對每個可能形成XSS的屬性進行特定的過濾。
現有的 XSS過濾模塊,一個是node-validator, 一個是@雷宗民寫的js-xss。
不可以保證 XSS模塊能夠防範住任意的XSS***,可是起碼可以過濾掉大部分可以想象到的漏洞。node-validator的XSS()仍然有bug,對於<p on="></p>形式的代碼,會有雙引號不閉合的問題,致使HTML元素測漏。
模版引擎致使的XSS***
cnode社區採用的是 ejs做爲模版引擎,而在ejs中,提供了兩種輸出動態數據到頁面的方法:
<% =data %> //進行 xss過濾的輸出
<% -data %> //不過濾直接輸出
而全部的過濾必須有一個前提: 模版文件中的 HTML屬性的值等,必須使用雙引號。 例如:
1.  <img src='<%= reply.author.avatar_url %>' title='<%= reply.author.name %>' />
2.  <img src="<%= reply.author.avatar_url %>" title="<%= reply.author.name %>" />
上面兩條語句,第一句因爲使用的是單引號,用戶能夠經過構造一個 avatar_url中帶單引號,來截斷src屬性,後面就能夠隨意加javascript代碼了。
CSRF ***
CSRF***在 node的web開發框架connect和express等中都有了解決方方案。經過在訪客的session中存放一個隨機的_csrf字段,模版引擎在生成HTML文件的時候將這個_csrf值傳遞到前端,訪客提交的任意POST請求,都必須帶上這個字段進行驗證,保證了只有當前用戶在當前頁面上能夠進行修改的操做。
然而當頁面存在 XSS漏洞的時候,CSRF的這種防範措施就成了浮雲。惡意***者徹底能夠經過javascript代碼,獲取到其餘用戶的_csrf值,並直接模擬用戶的POST請求進行服務端數據的更改。
相關文章
相關標籤/搜索