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