關于Web安全的問題,是一個老生常談的問題,作為離用戶最近的一層,我們大前端應該把手伸的更遠一點。
我們最常見的Web安全攻擊有以下幾種:
XSS
跨站腳本攻擊
CSRF
跨站請求偽造
URL
跳轉漏洞
ClickJacking
點擊劫持/UI-覆蓋攻擊
SQL Injection
SQL注入
OS Command Injection
OS命令注入
XSS (Cross Site script),中文是跨站腳本攻擊;其原本縮寫是 CSS,但為了和層疊樣式表(Cascading Style Sheet)有所區分,因而在安全領域叫做 XSS。
惡意攻擊者往Web頁面里插入惡意script代碼,當用戶瀏覽該頁之時,嵌入其中Web里面的script代碼會被執行,從而達到惡意攻擊用戶的目的。
XSS攻擊可以分為3類:
1. 反射型
反射型 XSS 只是簡單地把用戶輸入的數據 “反射” 給瀏覽器,這種攻擊方式往往需要攻擊者誘使用戶點擊一個惡意鏈接,或者提交一個表單,或者進入一個惡意網站時,注入腳本進入被攻擊者的網站。
假裝我是一個惡意鏈接(Click Me~)
1. const http = require('http');
2.
3. // 啟http服務
4. const server = http.createServer(function (req, res) {
5. res.setHeader('Access-Control-Allow-Origin', '*');
6. res.writeHead(200, {'Content-Type': 'text/html; charset=UTF-8'});
7. res.write('<script>while(true)alert("反射型 XSS 攻擊")</script>');
8. res.end();
9. });
10.
11.server.listen('8000');
這樣就產生了反射型 XSS 攻擊。攻擊者可以注入任意的惡意腳本進行攻擊,可能注入惡作劇腳本,或者注入能獲取用戶隱私數據(如cookie)的腳本,這取決于攻擊者的目的。
2. 存儲型
存儲型 XSS 會把用戶輸入的數據 "存儲" 在服務器端,當瀏覽器請求數據時,腳本從服務器上傳回并執行。這種 XSS 攻擊具有很強的穩定性。
比較常見的一個場景是攻擊者在社區或論壇上寫下一篇包含惡意 Javascript 代碼的文章或評論,文章或評論發表后,所有訪問該文章或評論的用戶,都會在他們的瀏覽器中執行這段惡意的 Javascript 代碼。
1. <!DOCTYPE html>
2. <html lang="en">
3. <head>
4. <meta charset="UTF-8">
5. <title>存儲型 XSS</title>
6. </head>
7. <body>
8. <div>Try Me:<input type="text" id="input"></div>
9. <button id="btn">Submit</button>
10. <script> const input = document.getElementById('input');
11. const btn = document.getElementById('btn');
12. let val;
13. input.addEventListener('change', e => {
14. val = e.target.value;
15. }, false);
16. btn.addEventListener('click', e => {
17. fetch('http://localhost:8000/save', {
18. method: 'POST',
19. body: val
20. });
21. }, false); </script>
22.</body>
23.</html>
1. const http = require('http');
2. let userInput = '';
3. function handleReequest (req, res) {
4. const method = req.method;
5. res.setHeader('Access-Control-Allow-Origin', '*');
6. res.setHeader('Access-Control-Allow-Headers', 'Content-Type')
7. if (method === 'POST' && req.url === '/save') {
8. let body = '';
9. req.on('data', chunk => {
10. body += chunk;
11. });
12. req.on('end', () => {
13. if (body) {
14. userInput = body;
15. }
16. res.end();
17. });
18. } else {
19. res.writeHead(200, { 'Content-Type': 'text/html; charset=UTF-8' });
20. res.write(userInput);
21. res.end();
22. }
23.}
24.
25.// 啟http服務
26.const server = http.createServer((req, res)=> {
27. handleReequest(req, res)
28.});
29.server.listen('8000');
3. 基于DOM
基于DOM或本地的XSS其實是一種特殊類型的反射型XSS,它是基于DOM文檔對象模型的一種漏洞??梢酝ㄟ^DOM來動態修改頁面內容,從客戶端獲取DOM中的數據并在本地執行。基于這個特性,就可以利用JS腳本來實現XSS漏洞的利用。
1. <body>
2. <input type="text" id="input">
3. <button id="btn">Submit</button>
4. <div id="div"></div>
5. <script> const input = document.getElementById('input');
6. const btn = document.getElementById('btn');
7. const div = document.getElementById('div');
8. let val;
9.
10. input.addEventListener('change', (e) => {
11. val = e.target.value;
12. }, false);
13.
14. btn.addEventListener('click', () => {
15. div.innerHTML = `<a href=${val}>Try Me~</a>`
16. }, false); </script>
17.</body>
總結: XSS攻擊的本質就是,利用一切手段在目標用戶的瀏覽器中執行攻擊腳本。
防范: 對于一切用戶的輸入、輸出、客戶端的輸出內容視為不可信,在數據添加到DOM或者執行了DOM API的時候,我們需要對內容進行HtmlEncode或JavascriptEncode,以預防XSS攻擊。
現在主流的瀏覽器內置了防范 XSS 的措施,例如 內容安全策略(CSP)。但對于開發者來說,也應該尋找可靠的解決方案來防止 XSS 攻擊。
二、CSRF
CSRF(Cross-site request forgery)跨站請求偽造,也被稱為“One Click Attack”或者Session Riding,通常縮寫為CSRF或者XSRF,是一種對網站的惡意利用。
通常情況下,CSRF 攻擊是攻擊者借助受害者的 Cookie 騙取服務器的信任,可以在受害者毫不知情的情況下以受害者名義偽造請求發送給受攻擊服務器,從而在并未授權的情況下執行在權限保護之下的操作。
關于瀏覽器的Cookie策略請參考HTTP Cookie。
1. 通過 Cookie 進行 CSRF 攻擊
假設有一個BBS站點http://www.a.com
當用戶登錄之后,會設置如下 cookie: res.setHeader('Set-Cookie', ['user=william; expires=Fri, 23 Mar 2019 00:00:00 GMT;'])
當登錄后的用戶發起 http://www.a.com/delete?id=666666
請求時,會刪除 id 為 666666 的帖子。
CSRF攻擊者準備的網站B:<img src="http://www.a.com/delete?id=666666">
當登錄用戶訪問攻擊者的網站B時,會向 www.a.com
發起一個刪除用戶帖子的請求。此時若用戶在切換到 www.a.com
的帖子頁面刷新,會發現ID 為 666666 的帖子已經被刪除。
要完成一次CSRF攻擊,受害者必須依次完成兩個步驟:
登錄受信任網站A,并在本地生成Cookie。
在不登出A的情況下,訪問危險網站B。
2. CSRF攻擊的防范
1.驗證 HTTP Referer 字段
2.添加 token 驗證
3.驗證碼
盡管CSRF聽起來像跨站腳本(XSS),但它與XSS非常不同,XSS利用站點內的信任用戶,而CSRF則通過偽裝來自受信任用戶的請求來利用受信任的網站。
與XSS攻擊相比,CSRF攻擊往往不大流行(因此對其進行防范的資源也相當稀少)和難以防范,所以被認為比XSS更具危險性,往往同XSS一同作案!
三、URL跳轉漏洞
借助未驗證的URL跳轉,將應用程序引導到不安全的第三方區域,從而導致的安全問題。
當用戶點擊后,經過服務器或者瀏覽器解析后,將會跳到惡意的網站中。
http://a.baidu.com/index?act=go&url=http://evil.cn/http://b.baidu.com/safecheck.html?id=1&url=http://evil.cn/http://c.baidu.com/f/user/passport?jumpUrl=http://evil.cn/
1. 實現方式
META標簽內跳轉
Javascript跳轉
Header頭跳轉
通過以GET或者POST的方式接收將要跳轉的URL,然后通過上面的幾種方式的其中一種來跳轉到目標URL。一方面,由于用戶的輸入會進入Meta,javascript,http頭所以都可能發生相應上下文的漏洞,如xss等等,但是同時,即使只是對于URL跳轉本身功能方面就存在一個缺陷,因為會將用戶瀏覽器從可信的站點導向到不可信的站點,同時如果跳轉的時候帶有敏感數據一樣可能將敏感數據泄漏給不可信的第三方。
2. 防御方案
① referer的限制
如果確定傳遞URL參數進入的來源,我們可以通過該方式實現安全限制,保證該URL的有效性,避免惡意用戶自己生成跳轉鏈接
② 加入有效性驗證Token
我們保證所有生成的鏈接都是來自于我們可信域的,通過在生成的鏈接里加入用戶不可控的Token對生成的鏈接進行校驗,可以避免用戶生成自己的惡意鏈接從而被利用,但是如果功能本身要求比較開放,可能導致有一定的限制。
四、ClickJacking
ClickJacking點擊劫持,也叫UI覆蓋攻擊,攻擊者會利用一個或多個透明或不透明的層來誘騙用戶支持點擊按鈕的操作,而實際的點擊確實用戶看不到的一個按鈕,從而達到在用戶不知情的情況下實施攻擊。
1. iframe覆蓋
這種攻擊方式的關鍵在于可以實現頁中頁的<iframe>
標簽,并且可以使用css樣式表將他不可見。
防止點擊劫持有兩種主要方法:
X-FRAME-OPTIONS
X-FRAME-OPTIONS是微軟提出的一個http響應首部,指示瀏覽器不允許從其他域進行取景,專門用來防御利用iframe嵌套的點擊劫持攻擊。并且在IE8、Firefox3.6、Chrome4以上的版本均能很好的支持。
DENY
: 拒絕任何域加載
SAMEORIGIN
: 允許同源域下加載
ALLOW-from
: 可以定義允許frame加載的頁面地址
頂層判斷
在UI中采用防御性代碼,以確保當前幀是最頂層的窗口,如: top != self || top.location != self.location || top.location != location
2. 圖片覆蓋
圖片覆蓋攻擊(Cross Site Image Overlaying),攻擊者使用一張或多張圖片,利用圖片的style或者能夠控制的CSS,將圖片覆蓋在網頁上,形成點擊劫持。當然圖片本身所帶的信息可能就帶有欺騙的含義,這樣不需要用戶點擊,也能達到欺騙的目的。
<a href="http://www.a.com/delete?id=666666"><img src="~~~" style="~~~" /></a>
解決方案: 在防御圖片覆蓋攻擊時,需要檢查用戶提交的HTML代碼中,img標簽的style屬性是否可能導致浮出。
五、SQL Injection
SQL 注入漏洞(SQL Injection)是 Web 開發中最常見的一種安全漏洞??梢杂盟鼇韽臄祿飓@取敏感信息,或者利用數據庫的特性執行添加用戶,導出文件等一系列惡意操作,甚至有可能獲取數據庫乃至系統用戶最高權限。
1. 原理
SQL注入攻擊指的是通過構建特殊的輸入作為參數傳入Web應用程序,而這些輸入大都是SQL語法里的一些組合,通過執行SQL語句進而執行攻擊者所要的操作,其主要原因是程序沒有細致地過濾用戶輸入的數據,致使非法數據侵入系統。
根據相關技術原理,SQL注入可以分為平臺層注入和代碼層注入。前者由不安全的數據庫配置或數據庫平臺的漏洞所致;后者主要是由于程序員對輸入未進行細致地過濾,從而執行了非法的數據查詢?;诖耍琒QL注入的產生原因通常表現在以下幾方面:
① 不當的類型處理;
② 不安全的數據庫配置;
③ 不合理的查詢集處理;
④ 不當的錯誤處理;
⑤ 轉義字符處理不合適;
⑥ 多個提交處理不當。
2. 攻擊
當應用程序使用輸入內容來構造動態sql語句以訪問數據庫時,會發生sql注入攻擊。如果代碼使用存儲過程,而這些存儲過程作為包含未篩選的用戶輸入的字符串來傳遞,也會發生sql注入。sql注入可能導致攻擊者使用應用程序登陸在數據庫中執行命令。相關的SQL注入可以通過測試工具pangolin進行。
3. 防護
① 永遠不要信任用戶的輸入。對用戶的輸入進行校驗,可以通過正則表達式,或限制長度;對單引號和雙"-"進行轉換等。
② 永遠不要使用動態拼裝sql,可以使用參數化的sql或者直接使用存詢存取。
③ 永遠不要使用管理員權限的數據庫連接,為每個應用使用單獨的權限有限的數據庫連接。
④ 不要把機密信息直接存放,加密或者hash掉密碼和敏感的信息。
⑤ 應用的異常信息應該給出盡可能少的提示,最好使用自定義的錯誤信息對原始錯誤信息進行包裝。
⑥ sql注入的檢測方法一般采取輔助軟件或網站平臺來檢測,軟件一般采用sql注入檢測工具jsky,網站平臺就有億思網站安全平臺檢測工具MDCSOFT SCAN等。采用MDCSOFT-IPS可以有效的防御SQL注入,XSS攻擊等。
六、OS命令注入
OS 注入攻擊是指程序提供了直接執行 Shell 命令的函數的場景,當攻擊者不合理使用,且開發者對用戶參數未考慮安全因素的話,就會執行惡意的命令調用,被攻擊者利用。
OS 命令注入其實原理和 SQL 注入是類似的,只是場景不一樣而已。 在 Node.js 中可以使用 exec()
執行命令通過傳入一段字符串命令,并把一個錯誤或命令處理結果回傳至回調函數中。
1. exec
let userInput = "user input";child_process.exec('ls -l ' + userInput, (err, data) => {console.log(data);});
攻擊者可以使用一個分號";"來結束命令,并開始一個新的調用,他們可以使用反引號或$()來運行子命令。還有很多潛在的濫用。
在child_process.exec引擎下,將調用執行"/bin/sh"。而不是目標程序。已發送的命令只是被傳遞給一個新的"/bin/ sh'進程來執行shell。 child_process.exec的名字有一定誤導性 - 這是一個bash的解釋器,而不是啟動一個程序。這意味著,如果直接執行用戶輸入的參數,所有的shell字符可能會產生毀滅性的后果。
2. execFile/spawn
在 Node.js 中除了 exec() 之外,還有 execFile() 和 spawn() 兩個方法也可以用來執行系統命令。它們和 exec() 的區別是后者是直接將一個命令字符串傳給 /bin/sh 執行,而前者是提供了一個數組作為參數容器,最后參數會被直接傳到 C 的命令執行方法 execve() 中,不容易執行額外的參數。
<!--child_process.execFile -->let path = "user input";child_process.execFile('/bin/ls', ['-l', path], (err, result) => {console.log(result)});
<!--child_process.spawn-->let path = "user input";let ls = child_process.spawn('/bin/ls', ['-l', path])ls.stdout.on('data', data => {console.log(data.toString());});
注意:
使用spawn或execFile并不總是安全的。例如,運行/bin/find,并傳入用戶輸入參數仍有可能導致系統被攻陷。 find命令有一些選項,允許讀/寫任意文件。
避免使用child_process.exec,當需要包含用戶輸入的參數時更是如此,請牢記。
盡量避免讓用戶傳入參數,使用選擇項比讓用戶直接輸入字符串要好得多。
必須允許用戶輸入參數的情況下,請廣泛參考該命令的參數,確定哪些選項是安全的,并建立一個白名單。
該文章在 2023/10/30 10:42:03 編輯過