瀏覽器專題系列 —— Web 安全問題
在現(xiàn)代網(wǎng)絡(luò)中,安全相關(guān)的問題是非常繁雜的,種類頗多
這里只介紹Web開發(fā)中經(jīng)常碰到的一些安全問題,同樣也是面試常考的一些內(nèi)容
SQL注入 XSS CSRF 點(diǎn)擊劫持 中間人攻擊
SQL注入
什么是SQL注入
后臺人員使用用戶輸入的數(shù)據(jù)進(jìn)行拼接組裝SQL查詢語句時,遇到惡意輸入就會返回不應(yīng)該返回的內(nèi)容
簡單示例
普通的查詢語句
SELECT?*?FROM?articles?WHERE?id?=?$id
1. 客戶端發(fā)送的正常請求
/api/articlres/article?id?=?12
拼接后的SQL
SELECT?*?FROM?articles?WHERE?id?=?12
2. 注入SQL發(fā)送的惡意請求
/api/articlres/article?id=1%20or%201=1
這里的 %20是空格經(jīng)過UrlEncode過后的內(nèi)容
拼接后的sql
SELECT?*?FROM?articles?WHERE?id?=?12?or?1=1
1=1 永遠(yuǎn)為真
這樣就擴(kuò)大了數(shù)據(jù)的查詢范圍,導(dǎo)致查詢異常或者返回原本不該返回的數(shù)據(jù)
如何防范
加入過濾和驗(yàn)證機(jī)制:
將參數(shù)的所有內(nèi)容當(dāng)做值,而不是當(dāng)做字符串的一部分 使用正則表達(dá)式過濾傳入的參數(shù) 檢查傳入內(nèi)容是否包含非法的關(guān)鍵字
XSS
什么是 XSS
XSS全稱Cross Site Scripting ,即跨站腳本攻擊
攻擊者可以將代碼注入頁面,然后可以進(jìn)行一系列損害用戶利益的事情
竊取Cookie 監(jiān)聽用戶行為 修改 DOM 偽造登錄表單 在頁面中生成浮窗廣告 惡意跳轉(zhuǎn) ...
惡意代碼未經(jīng)過濾,與網(wǎng)站正常的代碼混在一起,以至于瀏覽器無法分辨哪些腳本是可信的,導(dǎo)致惡意腳本被執(zhí)行
XSS 可以分為兩類:持久型和非持久型
持久型
持久型也就是攻擊的代碼被服務(wù)端寫入進(jìn)數(shù)據(jù)庫中,在頁面加載的時候執(zhí)行
常見于服務(wù)端渲染的時候出現(xiàn)
示例
html>
<html?lang="zh-cn">
<head>
????<title>Documenttitle>
head>
<body>
????<script?src="./test.js">script>
body>
html>
function?renderPage()?{
????//?ajax獲取數(shù)據(jù)?
????//?解析字符串生成對應(yīng)dom節(jié)點(diǎn)
????const?script?=?document.createElement('script')
????script.innerHTML?=?'alert(123)'
????document.body.appendChild(script)
}
renderPage()
非持久型
一般通過修改 URL 參數(shù)的方式加入攻擊代碼,誘導(dǎo)用戶訪問鏈接從而進(jìn)行攻擊
<div>{{name}}div>??
jquery觸發(fā)此錯誤示例
html>
<html?lang="en">
<head>
????<meta?charset="UTF-8">
????<title>Documenttitle>
????<script?src="https://cdn.staticfile.org/jquery/1.10.0/jquery.min.js">script>
head>
<body>
????<h1>大標(biāo)題h1>
????<ul?id="list">ul>
????<script?src="./test.js">script>
body>
html>
test.js
const?data?=?[
????'1',
????'2',
????''
]
data.forEach(str=>{
????$('#list').append(`${str} `)
})
如何防范
1.轉(zhuǎn)義字符
“不相信任何用戶的輸入
轉(zhuǎn)譯用戶輸入的內(nèi)容
function?escape(str)?{
??str?=?str.replace(/&/g,?'&')
??str?=?str.replace(/,?'<')
??str?=?str.replace(/>/g,?'>')
??str?=?str.replace(/"/g,?'&quto;')
??str?=?str.replace(/'/g,?''')
??str?=?str.replace(/`/g,?'`')
??str?=?str.replace(/\//g,?'/')
??return?str
}
或者利用現(xiàn)代瀏覽器的特性
自動處理特殊字符
function?filterStr(str)?{
????const?div?=?document.createElement('div')
????div.textContent?=?str
????return?div.innerHTML
}
filterStr('')?//?
2.CSP
CSP - 內(nèi)容安全策略
本質(zhì)上就是建立白名單,明確告訴瀏覽器哪些外部資源可以加載和執(zhí)行
可以通過這種方式來盡量減少 XSS 攻擊。
使用方式
設(shè)置 HTTP Header 中的 Content-Security-Policy 設(shè)置 meta 標(biāo)簽的方式
<meta?http-equiv="Content-Security-Policy"?content="script-src?'self';?object-src?'none';?style-src?cdn.example.org?third-party.org;?child-src?https:">
上述策略解釋
腳本:只信任當(dāng)前域名 標(biāo)簽:不信任任何URL,即不加載任何資源樣式表:只信任cdn.example.org和third-party.org 框架(frame):必須使用HTTPS協(xié)議加載 其他資源:沒有限制
示例:
html>
<html?lang="en">
<head>
????<meta?charset="UTF-8">
????<meta?name="viewport"?content="width=device-width,?initial-scale=1.0">
????<meta?http-equiv="Content-Security-Policy"
????????content="script-src?'self';?object-src?'none';?style-src?cdn.example.org?third-party.org;?child-src?https:">
????<script?src="https://cdn.staticfile.org/jquery/1.10.0/jquery.min.js">script>
????<title>Documenttitle>
head>
<body>
????<h1>測試h1>
????<script>
????????console.log($);
????script>
body>
html>
觀察瀏覽器的devtools面板會發(fā)現(xiàn)以下內(nèi)容

阻止第三方域名的腳本資源加載,阻止內(nèi)聯(lián)腳本執(zhí)行
3.防止腳本讀取Cookie
JavaScript提供了訪問cookie的API
可以通過document.cookie讀取與修改cookie
document.cookie
新增一個cookie
document.cookie?=?'sugar=at;expires=session'
執(zhí)行后我們可以在devtools面板的Application選項(xiàng)卡中看到我們添加的cookie

console.log(document.cookie)
//?可以看到我們剛才設(shè)置的cookie

目前很多統(tǒng)計(jì)網(wǎng)站的埋點(diǎn)監(jiān)控,權(quán)限控制,離線數(shù)據(jù)都依賴于cookie,第三方腳本很容易的能夠進(jìn)行竊取
可以為Cookie添加HttpOnly屬性,防止腳本讀取網(wǎng)站的cookie
添加Secure屬性要求必須使用https協(xié)議才能傳輸此cookie,可以防止中間人截獲修改到傳輸?shù)腸ookie
對于cookie的更多介紹可以查看 瀏覽器專題系列 - 本地存儲 這篇文章的介紹
CSRF
什么是CSRF
CSRF--Cross-site request forgery--跨站請求偽造
攻擊者構(gòu)造出一個請求鏈接,誘導(dǎo)用戶點(diǎn)擊或者通過某些途徑自動發(fā)起請求
主要利用的cookie會自動附帶在請求header中這個特性,很多網(wǎng)站使用cookie鑒權(quán)
如果當(dāng)前用戶是在登錄狀態(tài)下請求的此鏈接的話,服務(wù)端就以為是用戶在操作,從而進(jìn)行相應(yīng)的邏輯
發(fā)起手段
自動GET:使用img,link,script等等標(biāo)簽 自動POST:自動提交表單的腳本 誘導(dǎo)用戶點(diǎn)擊:iframe,a標(biāo)簽,透明的元素等
<img?src="http://a.b.com/api/xxx"/>
<link?href="http://a.b.com/api/xxx">
<script?src="http://a.b.com/api/xxx">script>
<script>
setTimeout(()=>{
????const?$form?=?document.createElement('form')
????$form.method?=?'POST'
????$form.action?=?'a.b.com/api/xxx'
????document.body.appendChild($form)
????$form.submit()
????$form.remove()
},0)
script>
如何防范
Get請求不對數(shù)據(jù)進(jìn)行修改,即無副作用操作 服務(wù)端過濾 Origin:域名信息 Referer:包含具體URL 開發(fā)者可通過自定義請求頭偽造 阻止第三方網(wǎng)站請求接口 驗(yàn)證request header中的 Referer/Origin:通過Referer驗(yàn)證請求是否為第三方發(fā)送 請求時附帶驗(yàn)證信息 添加驗(yàn)證碼參數(shù) request header中添加一個token字段 阻止第三方網(wǎng)站訪問到用戶 Cookie 設(shè)置Cookie的SameSite屬性:Cookie 隨跨域請求發(fā)送的策略 Strict:僅允許一方請求攜帶 Cookie,即瀏覽器將只發(fā)送相同站點(diǎn)請求的 Cookie,當(dāng)前網(wǎng)頁 URL 與請求目標(biāo) URL 完全一致才發(fā)送 Lax:允許部分(導(dǎo)航到目標(biāo)網(wǎng)址的 Get 請求)第三方請求攜帶 Cookie None:無論是否跨站都會發(fā)送 Cookie
點(diǎn)擊劫持
什么是點(diǎn)擊劫持
點(diǎn)擊劫持是一種視覺欺騙的攻擊手段
攻擊者將需要攻擊的網(wǎng)站通過 iframe 嵌套的方式嵌入自己的網(wǎng)頁中,并將 iframe 設(shè)置為透明,在頁面中透出一個按鈕誘導(dǎo)用戶點(diǎn)擊:此種方式最為常見,因?yàn)閕frame中可以嵌入用戶已經(jīng)登陸過的網(wǎng)頁 使用一個透明的綁定了事件元素附在正常元素上,誘導(dǎo)用戶點(diǎn)擊
如何防范
1. X-FRAME-OPTIONS
針對iframe形式的可通過設(shè)置X-FRAME-OPTIONS
X-FRAME-OPTIONS 是一個 HTTP 響應(yīng)頭
該響應(yīng)頭有三個值可選,分別是:
DENY,表示頁面不允許通過 iframe 的方式展示 SAMEORIGIN,表示頁面可以在相同域名下通過 iframe 的方式展示 ALLOW-FROM,表示頁面可以在指定來源的 iframe 中展示
2. JS 防御
window.self: 當(dāng)前 window 對象的引用 window.top: 最頂層的窗口對象 window.parent: 當(dāng)前窗口的直接父對象
在頁面中加入此代碼
<head>
??<style?id="click-jack">
????html?{
??????display:?none?!important;
????}
??style>
head>
<body>
??<script>
????if?(self?===?top)?{
??????var?style?=?document.getElementById('click-jack')
??????document.body.removeChild(style)
????}?else?{
??????top.location?=?self.location
????}
??script>
body>
中間人攻擊
什么是中間人攻擊
中間人攻擊是 攻擊方同時與服務(wù)端和客戶端建立起了連接,并讓對方認(rèn)為連接是安全的攻擊者不僅能獲得雙方的通信信息,還能修改通信信息
攻擊場景舉例:使用非對稱加密傳輸?shù)臄?shù)據(jù)
中間人已經(jīng)同時與客戶端與服務(wù)端建立了鏈接 服務(wù)端下發(fā)公鑰a1,中間人截獲,然后下發(fā)自己的公鑰b1 客戶端接收到公鑰b1,對傳輸?shù)臄?shù)據(jù) “你好” 加密成 “xxx”,然后發(fā)送給服務(wù)端 中間人收到數(shù)據(jù) “xxx”,用自己的私鑰B解密獲得內(nèi)容 “你好”,然后用截獲的a1公鑰 加密 內(nèi)容“滾滾滾”成 “yyy”,發(fā)送給服務(wù)端 服務(wù)端收到內(nèi)容 “yyy” 用自己的私鑰A解密 得到 “滾滾滾”的內(nèi)容
在這個過程中,中間人不僅獲取了用戶傳輸?shù)恼鎸?shí)數(shù)據(jù),還給服務(wù)的發(fā)送了錯誤的信息
如何防范
使用https
不要在公共Wi-Fi上發(fā)送敏感數(shù)據(jù)
使用權(quán)威機(jī)構(gòu)的CA證書
參考資料
簡書 - sql注入基礎(chǔ)原理(超詳細(xì)): https://www.jianshu.com/p/078df7a35671
[2]MDN:Content-Security-Policy: https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Content-Security-Policy
[3]阮一峰:CSP: http://www.ruanyifeng.com/blog/2016/09/csp.html
