Web 安全地對(duì)于 Web 從業(yè)人員來說是一個(gè)非常重要的課題,所以在這里總結(jié)一下 Web 相關(guān)的安全攻防知識(shí),希望以后不要再踩雷,也希望對(duì)看到這篇文章的同學(xué)有所幫助。今天這邊文章主要的內(nèi)容就是分析幾種常見的攻擊的類型以及防御的方法。
也許你對(duì)所有的安全問題都有一定的認(rèn)識(shí),但最主要的還是在編碼設(shè)計(jì)的過程中時(shí)刻繃緊安全那根弦,需要反復(fù)推敲每個(gè)實(shí)現(xiàn)細(xì)節(jié),安全無小事。
本文代碼 Demo 都是基于 Node.js 講解,其他服務(wù)端語言同樣可以參考。
XSS
首先說下最常見的 XSS 漏洞,XSS (Cross Site Script),跨站腳本攻擊,因?yàn)榭s寫和 css (Cascading Style Sheets) 重疊,所以只能叫 XSS。
XSS 的原理是惡意攻擊者往 Web 頁面里插入惡意可執(zhí)行網(wǎng)頁腳本代碼,當(dāng)用戶瀏覽該頁之時(shí),嵌入其中 Web 里面的腳本代碼會(huì)被執(zhí)行,從而可以達(dá)到攻擊者盜取用戶信息或其他侵犯用戶安全隱私的目的。XSS 的攻擊方式千變?nèi)f化,但還是可以大致細(xì)分為幾種類型。
非持久型 XSS
非持久型 XSS 漏洞,也叫反射型 XSS 漏洞,一般是通過給別人發(fā)送帶有惡意腳本代碼參數(shù)的 URL,當(dāng) URL 地址被打開時(shí),特有的惡意代碼參數(shù)被 html 解析、執(zhí)行。

舉一個(gè)例子,比如你的 Web 頁面中包含有以下代碼:
|
|
攻擊者可以直接通過 URL 注入可執(zhí)行的腳本代碼。
非持久型 XSS 漏洞攻擊有以下幾點(diǎn)特征:
- 即時(shí)性,不經(jīng)過服務(wù)器存儲(chǔ),直接通過 HTTP 的 GET 和 POST 請(qǐng)求就能完成一次攻擊,拿到用戶隱私數(shù)據(jù)。
- 攻擊者需要誘騙點(diǎn)擊
- 反饋率低,所以較難發(fā)現(xiàn)和響應(yīng)修復(fù)
- 盜取用戶敏感保密信息
為了防止出現(xiàn)非持久型 XSS 漏洞,需要確保這么幾件事情:
- Web 頁面渲染的所有內(nèi)容或者渲染的數(shù)據(jù)都必須來自于服務(wù)端。
- 盡量不要從 URL,document.referrer,document.forms 等這種 DOM API 中獲取數(shù)據(jù)直接渲染。
- 盡量不要使用 eval, new Function(),document.write(),document.writeln(),window.setInterval(),window.setTimeout(),innerHTML,document.creteElement() 等可執(zhí)行字符串的方法。
- 如果做不到以上幾點(diǎn),也必須對(duì)涉及 DOM 渲染的方法傳入的字符串參數(shù)做 escape 轉(zhuǎn)義。
- 前端渲染的時(shí)候?qū)θ魏蔚淖侄味夹枰?escape 轉(zhuǎn)義編碼。
escape 轉(zhuǎn)義的目的是將一些構(gòu)成 HTML 標(biāo)簽的元素轉(zhuǎn)義,比如 <,>,空格 等,轉(zhuǎn)義成 <,>, 等顯示轉(zhuǎn)義字符。有很多開源的工具可以協(xié)助我們做 escape 轉(zhuǎn)義。
持久型 XSS
持久型 XSS 漏洞,也被稱為存儲(chǔ)型 XSS 漏洞,一般存在于 Form 表單提交等交互功能,如發(fā)帖留言,提交文本信息等,黑客利用的 XSS 漏洞,將內(nèi)容經(jīng)正常功能提交進(jìn)入數(shù)據(jù)庫持久保存,當(dāng)前端頁面獲得后端從數(shù)據(jù)庫中讀出的注入代碼時(shí),恰好將其渲染執(zhí)行。
主要注入頁面方式和非持久型 XSS 漏洞類似,只不過持久型的不是來源于 URL,refferer,forms 等,而是來源于后端從數(shù)據(jù)庫中讀出來的數(shù)據(jù)。持久型 XSS 攻擊不需要誘騙點(diǎn)擊,黑客只需要在提交表單的地方完成注入即可,但是這種 XSS 攻擊的成本相對(duì)還是很高。攻擊成功需要同時(shí)滿足以下幾個(gè)條件:
- POST 請(qǐng)求提交表單后端沒做轉(zhuǎn)義直接入庫。
- 后端從數(shù)據(jù)庫中取出數(shù)據(jù)沒做轉(zhuǎn)義直接輸出給前端。
- 前端拿到后端數(shù)據(jù)沒做轉(zhuǎn)義直接渲染成 DOM。
持久型 XSS 有以下幾個(gè)特點(diǎn):
- 持久性,植入在數(shù)據(jù)庫中
- 危害面廣,甚至可以讓用戶機(jī)器變成 DDoS 攻擊的肉雞。
- 盜取用戶敏感私密信息
為了防止持久型 XSS 漏洞,需要前后端共同努力:
- 后端在入庫前應(yīng)該選擇不相信任何前端數(shù)據(jù),將所有的字段統(tǒng)一進(jìn)行轉(zhuǎn)義處理。
- 后端在輸出給前端數(shù)據(jù)統(tǒng)一進(jìn)行轉(zhuǎn)義處理。
- 前端在渲染頁面 DOM 的時(shí)候應(yīng)該選擇不相信任何后端數(shù)據(jù),任何字段都需要做轉(zhuǎn)義處理。
基于字符集的 XSS
其實(shí)現(xiàn)在很多的瀏覽器以及各種開源的庫都專門針對(duì)了 XSS 進(jìn)行轉(zhuǎn)義處理,盡量默認(rèn)抵御絕大多數(shù) XSS 攻擊,但是還是有很多方式可以繞過轉(zhuǎn)義規(guī)則,讓人防不勝防。比如「基于字符集的 XSS 攻擊」就是繞過這些轉(zhuǎn)義處理的一種攻擊方式,比如有些 Web 頁面字符集不固定,用戶輸入非期望字符集的字符,有時(shí)會(huì)繞過轉(zhuǎn)義過濾規(guī)則。
以基于 utf-7 的 XSS 為例
utf-7 是可以將所有的 unicode 通過 7bit 來表示的一種字符集 (但現(xiàn)在已經(jīng)從 Unicode 規(guī)格中移除)。
這個(gè)字符集為了通過 7bit 來表示所有的文字, 除去數(shù)字和一部分的符號(hào),其它的部分將都以 base64 編碼為基礎(chǔ)的方式呈現(xiàn)。
|
|
可以形成「基于字符集的 XSS 攻擊」的原因是由于瀏覽器在 meta 沒有指定 charset 的時(shí)候有自動(dòng)識(shí)別編碼的機(jī)制,所以這類攻擊通常就是發(fā)生在沒有指定或者沒來得及指定 meta 標(biāo)簽的 charset 的情況下。
所以我們有什么辦法避免這種 XSS 呢?
- 記住指定 <meta charset="utf-8">
- XML 中不僅要指定字符集為 utf-8,而且標(biāo)簽要閉合
基于 Flash 的跨站 XSS
基于 Flash 的跨站 XSS 也是屬于反射型 XSS 的一種,雖然現(xiàn)在開發(fā) ActionScript 的產(chǎn)品線幾乎沒有了,但還是提一句吧,AS 腳本可以接受用戶輸入并操作 cookie,攻擊者可以配合其他 XSS(持久型或者非持久型)方法將惡意 swf 文件嵌入頁面中。主要是因?yàn)?AS 有時(shí)候需要和 JS 傳參交互,攻擊者會(huì)通過惡意的 XSS 注入篡改參數(shù),竊取并操作cookie。
避免方法:
- 嚴(yán)格管理 cookie 的讀寫權(quán)限
- 對(duì) Flash 能接受用戶輸入的參數(shù)進(jìn)行過濾 escape 轉(zhuǎn)義處理
未經(jīng)驗(yàn)證的跳轉(zhuǎn) XSS
有一些場景是后端需要對(duì)一個(gè)傳進(jìn)來的待跳轉(zhuǎn)的 URL 參數(shù)進(jìn)行一個(gè) 302 跳轉(zhuǎn),可能其中會(huì)帶有一些用戶的敏感(cookie)信息。如果服務(wù)器端做302 跳轉(zhuǎn),跳轉(zhuǎn)的地址來自用戶的輸入,攻擊者可以輸入一個(gè)惡意的跳轉(zhuǎn)地址來執(zhí)行腳本。
這時(shí)候需要通過以下方式來防止這類漏洞:
- 對(duì)待跳轉(zhuǎn)的 URL 參數(shù)做白名單或者某種規(guī)則過濾
- 后端注意對(duì)敏感信息的保護(hù), 比如 cookie 使用來源驗(yàn)證。
CSRF
CSRF(Cross-Site Request Forgery),中文名稱:跨站請(qǐng)求偽造攻擊
那么 CSRF 到底能夠干嘛呢?你可以這樣簡單的理解:攻擊者可以盜用你的登陸信息,以你的身份模擬發(fā)送各種請(qǐng)求。攻擊者只要借助少許的社會(huì)工程學(xué)得詭計(jì),例如通過 QQ 等聊天軟件發(fā)送的鏈接(有些還偽裝成短域名,用戶無法分辨),攻擊者就能迫使 Web 應(yīng)用的用戶去執(zhí)行攻擊者預(yù)設(shè)的操作。例如,當(dāng)用戶登錄網(wǎng)絡(luò)銀行去查看其存款余額,在他沒有退出時(shí),就點(diǎn)擊了一個(gè) QQ 好友發(fā)來的鏈接,那么該用戶銀行帳戶中的資金就有可能被轉(zhuǎn)移到攻擊者指定的帳戶中。
所以遇到 CSRF 攻擊時(shí),將對(duì)終端用戶的數(shù)據(jù)和操作指令構(gòu)成嚴(yán)重的威脅。當(dāng)受攻擊的終端用戶具有管理員帳戶的時(shí)候,CSRF 攻擊將危及整個(gè) Web 應(yīng)用程序。
CSRF 原理
下圖大概描述了 CSRF 攻擊的原理,可以理解為有一個(gè)小偷在你配鑰匙的地方得到了你家的鑰匙,然后拿著要是去你家想偷什么偷什么。

完成 CSRF 攻擊必須要有三個(gè)條件:
- 用戶已經(jīng)登錄了站點(diǎn) A,并在本地記錄了 cookie
- 在用戶沒有登出站點(diǎn) A 的情況下(也就是 cookie 生效的情況下),訪問了惡意攻擊者提供的引誘危險(xiǎn)站點(diǎn) B (B 站點(diǎn)要求訪問站點(diǎn)A)。
- 站點(diǎn) A 沒有做任何 CSRF 防御
你也許會(huì)問:「如果我不滿足以上三個(gè)條件中的任意一個(gè),就不會(huì)受到 CSRF 的攻擊」。其實(shí)可以這么說的,但你不能保證以下情況不會(huì)發(fā)生:
- 你不能保證你登錄了一個(gè)網(wǎng)站后,不再打開一個(gè) tab 頁面并訪問另外的網(wǎng)站,特別現(xiàn)在瀏覽器都是支持多 tab 的。
- 你不能保證你關(guān)閉瀏覽器了后,你本地的 cookie 立刻過期,你上次的會(huì)話已經(jīng)結(jié)束。
- 上圖中所謂的攻擊網(wǎng)站 B,可能是一個(gè)存在其他漏洞的可信任的經(jīng)常被人訪問的網(wǎng)站。
預(yù)防 CSRF
CSRF 的防御可以從服務(wù)端和客戶端兩方面著手,防御效果是從服務(wù)端著手效果比較好,現(xiàn)在一般的 CSRF 防御也都在服務(wù)端進(jìn)行。服務(wù)端的預(yù)防 CSRF 攻擊的方式方法有多種,但思路上都是差不多的,主要從以下兩個(gè)方面入手:
- 正確使用 GET,POST 請(qǐng)求和 cookie
- 在非 GET 請(qǐng)求中增加 token
一般而言,普通的 Web 應(yīng)用都是以 GET、POST 請(qǐng)求為主,還有一種請(qǐng)求是 cookie 方式。我們一般都是按照如下規(guī)則設(shè)計(jì)應(yīng)用的請(qǐng)求:
- GET 請(qǐng)求常用在查看,列舉,展示等不需要改變資源屬性的時(shí)候(數(shù)據(jù)庫 query 查詢的時(shí)候)
- POST 請(qǐng)求常用在 From 表單提交,改變一個(gè)資源的屬性或者做其他一些事情的時(shí)候(數(shù)據(jù)庫有 insert、update、delete 的時(shí)候)
當(dāng)正確的使用了 GET 和 POST 請(qǐng)求之后,剩下的就是在非 GET 方式的請(qǐng)求中增加隨機(jī)數(shù),這個(gè)大概有三種方式來進(jìn)行:
- 為每個(gè)用戶生成一個(gè)唯一的 cookie token,所有表單都包含同一個(gè)偽隨機(jī)值,這種方案最簡單,因?yàn)楣粽卟荒塬@得第三方的 cookie(理論上),所以表單中的數(shù)據(jù)也就構(gòu)造失敗,但是由于用戶的 cookie 很容易由于網(wǎng)站的 XSS 漏洞而被盜取,所以這個(gè)方案必須要在沒有 XSS 的情況下才安全。
- 每個(gè) POST 請(qǐng)求使用驗(yàn)證碼,這個(gè)方案算是比較完美的,但是需要用戶多次輸入驗(yàn)證碼,用戶體驗(yàn)比較差,所以不適合在業(yè)務(wù)中大量運(yùn)用。
- 渲染表單的時(shí)候,為每一個(gè)表單包含一個(gè) csrfToken,提交表單的時(shí)候,帶上 csrfToken,然后在后端做 csrfToken 驗(yàn)證。
CSRF 的防御可以根據(jù)應(yīng)用場景的不同自行選擇。CSRF 的防御工作確實(shí)會(huì)在正常業(yè)務(wù)邏輯的基礎(chǔ)上帶來很多額外的開發(fā)量,但是這種工作量是值得的,畢竟用戶隱私以及財(cái)產(chǎn)安全是產(chǎn)品最基礎(chǔ)的根本。
SQL 注入
SQL 注入漏洞(SQL Injection)是 Web 開發(fā)中最常見的一種安全漏洞。可以用它來從數(shù)據(jù)庫獲取敏感信息,或者利用數(shù)據(jù)庫的特性執(zhí)行添加用戶,導(dǎo)出文件等一系列惡意操作,甚至有可能獲取數(shù)據(jù)庫乃至系統(tǒng)用戶最高權(quán)限。
而造成 SQL 注入的原因是因?yàn)槌绦驔]有有效的轉(zhuǎn)義過濾用戶的輸入,使攻擊者成功的向服務(wù)器提交惡意的 SQL 查詢代碼,程序在接收后錯(cuò)誤的將攻擊者的輸入作為查詢語句的一部分執(zhí)行,導(dǎo)致原始的查詢邏輯被改變,額外的執(zhí)行了攻擊者精心構(gòu)造的惡意代碼。
很多 Web 開發(fā)者沒有意識(shí)到 SQL 查詢是可以被篡改的,從而把 SQL 查詢當(dāng)作可信任的命令。殊不知,SQL 查詢是可以繞開訪問控制,從而繞過身份驗(yàn)證和權(quán)限檢查的。更有甚者,有可能通過 SQL 查詢?nèi)ミ\(yùn)行主機(jī)系統(tǒng)級(jí)的命令。
SQL 注入原理
下面將通過一些真實(shí)的例子來詳細(xì)講解 SQL 注入的方式的原理。
考慮以下簡單的管理員登錄表單:
|
|
后端的 SQL 語句可能是如下這樣的:
|
|
目的就是來驗(yàn)證用戶名和密碼是不是正確,按理說乍一看上面的 SQL 語句也沒什么毛病,確實(shí)是能夠達(dá)到我們的目的,可是你只是站在用戶會(huì)老老實(shí)實(shí)按照你的設(shè)計(jì)來輸入的角度來看問題,如果有一個(gè)惡意攻擊者輸入的用戶名是 zoumiaojiang' OR 1 = 1 --,密碼隨意輸入,就可以直接登入系統(tǒng)了。WFT!
冷靜下來思考一下,我們之前預(yù)想的真實(shí) SQL 語句是:
|
|
可以惡意攻擊者的奇怪用戶名將你的 SQL 語句變成了如下形式:
|
|
在 SQL 中,-- 是注釋后面的內(nèi)容的意思,所以查詢語句就變成了:
|
|
這條 SQL 語句的查詢條件永遠(yuǎn)為真,所以意思就是惡意攻擊者不用我的密碼,就可以登錄進(jìn)我的賬號(hào),然后可以在里面為所欲為,然而這還只是最簡單的注入,牛逼的 SQL 注入高手甚至可以通過 SQL 查詢?nèi)ミ\(yùn)行主機(jī)系統(tǒng)級(jí)的命令,將你主機(jī)里的內(nèi)容一覽無余,這里我也沒有這個(gè)能力講解的太深入,畢竟不是專業(yè)研究這類攻擊的,但是通過以上的例子,已經(jīng)了解了 SQL 注入的原理,我們基本已經(jīng)能找到防御 SQL 注入的方案了。
如何預(yù)防 SQL 注入
防止 SQL 注入主要是不能允許用戶輸入的內(nèi)容影響正常的 SQL 語句的邏輯,當(dāng)用戶的輸入的信息將要用來拼接 SQL 語句的話,我們應(yīng)該永遠(yuǎn)選擇不相信,任何內(nèi)容都必須進(jìn)行轉(zhuǎn)義過濾,當(dāng)然做到這個(gè)還是不夠的,下面列出防御 SQL 注入的幾點(diǎn)注意事項(xiàng):
- 嚴(yán)格限制Web應(yīng)用的數(shù)據(jù)庫的操作權(quán)限,給此用戶提供僅僅能夠滿足其工作的最低權(quán)限,從而最大限度的減少注入攻擊對(duì)數(shù)據(jù)庫的危害
- 后端代碼檢查輸入的數(shù)據(jù)是否符合預(yù)期,嚴(yán)格限制變量的類型,例如使用正則表達(dá)式進(jìn)行一些匹配處理。
- 對(duì)進(jìn)入數(shù)據(jù)庫的特殊字符(',",,<,>,&,*,; 等)進(jìn)行轉(zhuǎn)義處理,或編碼轉(zhuǎn)換。基本上所有的后端語言都有對(duì)字符串進(jìn)行轉(zhuǎn)義處理的方法,比如 lodash 的 lodash._escapehtmlchar 庫。
- 所有的查詢語句建議使用數(shù)據(jù)庫提供的參數(shù)化查詢接口,參數(shù)化的語句使用參數(shù)而不是將用戶輸入變量嵌入到 SQL 語句中,即不要直接拼接 SQL 語句。例如 Node.js 中的 MySQLjs 庫的 query 方法中的 ? 占位參數(shù)。
|
|
- 在應(yīng)用發(fā)布之前建議使用專業(yè)的 SQL 注入檢測工具進(jìn)行檢測,以及時(shí)修補(bǔ)被發(fā)現(xiàn)的 SQL 注入漏洞。網(wǎng)上有很多這方面的開源工具,例如 sqlmap、SQLninja 等。
- 避免網(wǎng)站打印出 SQL 錯(cuò)誤信息,比如類型錯(cuò)誤、字段不匹配等,把代碼里的 SQL 語句暴露出來,以防止攻擊者利用這些錯(cuò)誤信息進(jìn)行 SQL 注入。
- 不要過于細(xì)化返回的錯(cuò)誤信息,如果目的是方便調(diào)試,就去使用后端日志,不要在接口上過多的暴露出錯(cuò)信息,畢竟真正的用戶不關(guān)心太多的技術(shù)細(xì)節(jié),只要話術(shù)合理就行。
碰到要操作的數(shù)據(jù)庫的代碼,一定要慎重,小心使得萬年船,多找?guī)讉€(gè)人多來幾次 code review,將問題都暴露出來,而且要善于利用工具,操作數(shù)據(jù)庫相關(guān)的代碼屬于機(jī)密,沒事不要去各種論壇曬自家站點(diǎn)的 SQL 語句,萬一被人盯上了呢?
命令行注入
命令行注入漏洞,指的是攻擊者能夠通過 HTTP 請(qǐng)求直接侵入主機(jī),執(zhí)行攻擊者預(yù)設(shè)的 shell 命令,聽起來好像匪夷所思,這往往是 Web 開發(fā)者最容易忽視但是卻是最危險(xiǎn)的一個(gè)漏洞之一,看一個(gè)實(shí)例:
假如現(xiàn)在需要實(shí)現(xiàn)一個(gè)需求:用戶提交一些內(nèi)容到服務(wù)器,然后在服務(wù)器執(zhí)行一些系統(tǒng)命令去產(chǎn)出一個(gè)結(jié)果返回給用戶,接口的部分實(shí)現(xiàn)如下:
|
|
這段代碼確實(shí)能夠滿足業(yè)務(wù)需求,正常的用戶也確實(shí)能從指定的 git repo 上下載到想要的代碼,可是和 SQL 注入一樣,這段代碼在惡意攻擊者眼中,簡直就是香餑餑。
如果 params.repo 傳入的是
https://github.com/zoumiaojiang/zoumiaojiang.github.io.git 當(dāng)然沒問題了。
可是如果 params.repo 傳入的是
https://github.com/xx/xx.git && rm -rf /* && 恰好你的服務(wù)是用 root 權(quán)限起的就慘了。
具體惡意攻擊者能用命令行注入干什么也像 SQL 注入一樣,手法是千變?nèi)f化的,比如「反彈 shell 注入」等,但原理都是一樣的,我們絕對(duì)有能力防止命令行注入發(fā)生。防止命令行注入需要做到以下幾件事情:
- 后端對(duì)前端提交內(nèi)容需要完全選擇不相信,并且對(duì)其進(jìn)行規(guī)則限制(比如正則表達(dá)式)。
- 在調(diào)用系統(tǒng)命令前對(duì)所有傳入?yún)?shù)進(jìn)行命令行參數(shù)轉(zhuǎn)義過濾。
- 不要直接拼接命令語句,借助一些工具做拼接、轉(zhuǎn)義預(yù)處理,例如 Node.js 的 shell-escape npm 包。
還是前面的例子,我們可以做到如下:
|
|
無論是在何種后端語言環(huán)境中,凡是涉及到代碼調(diào)用系統(tǒng) shell 命令的時(shí)候都一定要謹(jǐn)慎。
DDoS 攻擊
DDoS 又叫分布式拒絕服務(wù),全稱 Distributed Denial of Service,其原理就是利用大量的請(qǐng)求造成資源過載,導(dǎo)致服務(wù)不可用,這個(gè)攻擊應(yīng)該不能算是安全問題,這應(yīng)該算是一個(gè)另類的存在,因?yàn)檫@種攻擊根本就是耍流氓的存在,「傷敵一千,自損八百」的行為。出于保護(hù) Web App 不受攻擊的攻防角度,還是介紹一下 DDoS 攻擊吧,畢竟也是挺常見的。
DDoS 攻擊可以理解為:「你開了一家店,隔壁家點(diǎn)看不慣,就雇了一大堆黑社會(huì)人員進(jìn)你店里干坐著,也不消費(fèi),其他客人也進(jìn)不來,導(dǎo)致你營業(yè)慘淡」。為啥說 DDoS 是個(gè)「傷敵一千,自損八百」的行為呢?畢竟隔壁店還是花了不少錢雇黑社會(huì)但是啥也沒得到不是?DDoS 攻擊的目的基本上就以下幾個(gè):
- 深仇大恨,就是要干死你
- 敲詐你,不給錢就干你
- 忽悠你,不買我防火墻服務(wù)就會(huì)有“人”繼續(xù)干你
也許你的站點(diǎn)遭受過 DDoS 攻擊,具體什么原因怎么解讀見仁見智。DDos 攻擊從層次上可分為網(wǎng)絡(luò)層攻擊與應(yīng)用層攻擊,從攻擊手法上可分為快型流量攻擊與慢型流量攻擊,但其原理都是造成資源過載,導(dǎo)致服務(wù)不可用。
網(wǎng)絡(luò)層 DDoS
網(wǎng)絡(luò)層 DDos 攻擊包括 SYN Flood、ACK Flood、UDP Flood、ICMP Flood 等。
SYN Flood 攻擊
SYN flood 攻擊主要利用了 TCP 三次握手過程中的 Bug,我們都知道 TCP 三次握手過程是要建立連接的雙方發(fā)送 SYN,SYN + ACK,ACK 數(shù)據(jù)包,而當(dāng)攻擊方隨意構(gòu)造源 IP 去發(fā)送 SYN 包時(shí),服務(wù)器返回的 SYN + ACK 就不能得到應(yīng)答(因?yàn)?IP 是隨意構(gòu)造的),此時(shí)服務(wù)器就會(huì)嘗試重新發(fā)送,并且會(huì)有至少 30s 的等待時(shí)間,導(dǎo)致資源飽和服務(wù)不可用,此攻擊屬于慢性 DDoS 攻擊。
ACK Flood 攻擊
ACK Flood 攻擊是在 TCP 連接建立之后,所有的數(shù)據(jù)傳輸 TCP 報(bào)文都是帶有 ACK 標(biāo)志位的,主機(jī)在接收到一個(gè)帶有 ACK 標(biāo)志位的數(shù)據(jù)包的時(shí)候,需要檢查該數(shù)據(jù)包所表示的連接四元組是否存在,如果存在則檢查該數(shù)據(jù)包所表示的狀態(tài)是否合法,然后再向應(yīng)用層傳遞該數(shù)據(jù)包。如果在檢查中發(fā)現(xiàn)該數(shù)據(jù)包不合法,例如該數(shù)據(jù)包所指向的目的端口在本機(jī)并未開放,則主機(jī)操作系統(tǒng)協(xié)議棧會(huì)回應(yīng) RST 包告訴對(duì)方此端口不存在。
UDP Flood 攻擊
UDP flood 攻擊是由于 UDP 是一種無連接的協(xié)議,因此攻擊者可以偽造大量的源 IP 地址去發(fā)送 UDP 包,此種攻擊屬于大流量攻擊。正常應(yīng)用情況下,UDP 包雙向流量會(huì)基本相等,因此發(fā)起這種攻擊的攻擊者在消耗對(duì)方資源的時(shí)候也在消耗自己的資源。
ICMP Flood 攻擊
ICMP Flood 攻擊屬于大流量攻擊,其原理就是不斷發(fā)送不正常的 ICMP 包(所謂不正常就是 ICMP 包內(nèi)容很大),導(dǎo)致目標(biāo)帶寬被占用,但其本身資源也會(huì)被消耗。目前很多服務(wù)器都是禁 ping 的(在防火墻外可以屏蔽 ICMP 包),因此這種攻擊方式已經(jīng)落伍。
網(wǎng)絡(luò)層 DDoS 防御
網(wǎng)絡(luò)層的 DDoS 攻擊究其本質(zhì)其實(shí)是無法防御的,我們能做得就是不斷優(yōu)化服務(wù)本身部署的網(wǎng)絡(luò)架構(gòu),以及提升網(wǎng)絡(luò)帶寬。當(dāng)然,還是做好以下幾件事也是有助于緩解網(wǎng)絡(luò)層 DDoS 攻擊的沖擊:
- 網(wǎng)絡(luò)架構(gòu)上做好優(yōu)化,采用負(fù)載均衡分流。
- 確保服務(wù)器的系統(tǒng)文件是最新的版本,并及時(shí)更新系統(tǒng)補(bǔ)丁。
- 添加抗 DDos 設(shè)備,進(jìn)行流量清洗。
- 限制同時(shí)打開的 SYN 半連接數(shù)目,縮短 SYN 半連接的 Timeout 時(shí)間。
- 限制單 IP 請(qǐng)求頻率。
- 防火墻等防護(hù)設(shè)置禁止 ICMP 包等。
- 嚴(yán)格限制對(duì)外開放的服務(wù)器的向外訪問。
- 運(yùn)行端口映射程序或端口掃描程序,要認(rèn)真檢查特權(quán)端口和非特權(quán)端口。
- 關(guān)閉不必要的服務(wù)。
- 認(rèn)真檢查網(wǎng)絡(luò)設(shè)備和主機(jī)/服務(wù)器系統(tǒng)的日志。只要日志出現(xiàn)漏洞或是時(shí)間變更,那這臺(tái)機(jī)器就可能遭到了攻擊。
- 限制在防火墻外與網(wǎng)絡(luò)文件共享。這樣會(huì)給黑客截取系統(tǒng)文件的機(jī)會(huì),主機(jī)的信息暴露給黑客,無疑是給了對(duì)方入侵的機(jī)會(huì)。
- 加錢堆機(jī)器。。
- 報(bào)警。。
應(yīng)用層 DDoS
應(yīng)用層 DDoS 攻擊不是發(fā)生在網(wǎng)絡(luò)層,是發(fā)生在 TCP 建立握手成功之后,應(yīng)用程序處理請(qǐng)求的時(shí)候,現(xiàn)在很多常見的 DDoS 攻擊都是應(yīng)用層攻擊。應(yīng)用層攻擊千變?nèi)f化,目的就是在網(wǎng)絡(luò)應(yīng)用層耗盡你的帶寬,下面列出集中典型的攻擊類型。
CC 攻擊
當(dāng)時(shí)綠盟為了防御 DDoS 攻擊研發(fā)了一款叫做 Collapasar 的產(chǎn)品,能夠有效的防御 SYN Flood 攻擊。黑客為了挑釁,研發(fā)了一款 Challenge Collapasar 攻擊工具(簡稱 CC)。
CC 攻擊的原理,就是針對(duì)消耗資源比較大的頁面不斷發(fā)起不正常的請(qǐng)求,導(dǎo)致資源耗盡。因此在發(fā)送 CC 攻擊前,我們需要尋找加載比較慢,消耗資源比較多的網(wǎng)頁,比如需要查詢數(shù)據(jù)庫的頁面、讀寫硬盤文件的等。通過 CC 攻擊,使用爬蟲對(duì)某些加載需要消耗大量資源的頁面發(fā)起 HTTP 請(qǐng)求。
DNS Flood
DNS Flood 攻擊采用的方法是向被攻擊的服務(wù)器發(fā)送大量的域名解析請(qǐng)求,通常請(qǐng)求解析的域名是隨機(jī)生成或者是網(wǎng)絡(luò)世界上根本不存在的域名,被攻擊的DNS 服務(wù)器在接收到域名解析請(qǐng)求的時(shí)候首先會(huì)在服務(wù)器上查找是否有對(duì)應(yīng)的緩存,如果查找不到并且該域名無法直接由服務(wù)器解析的時(shí)候,DNS 服務(wù)器會(huì)向其上層 DNS 服務(wù)器遞歸查詢域名信息。域名解析的過程給服務(wù)器帶來了很大的負(fù)載,每秒鐘域名解析請(qǐng)求超過一定的數(shù)量就會(huì)造成 DNS 服務(wù)器解析域名超時(shí)。
根據(jù)微軟的統(tǒng)計(jì)數(shù)據(jù),一臺(tái) DNS 服務(wù)器所能承受的動(dòng)態(tài)域名查詢的上限是每秒鐘 9000 個(gè)請(qǐng)求。而我們知道,在一臺(tái) P3 的 PC 機(jī)上可以輕易地構(gòu)造出每秒鐘幾萬個(gè)域名解析請(qǐng)求,足以使一臺(tái)硬件配置極高的 DNS 服務(wù)器癱瘓,由此可見 DNS 服務(wù)器的脆弱性。
HTTP 慢速連接攻擊
針對(duì) HTTP 協(xié)議,先建立起 HTTP 連接,設(shè)置一個(gè)較大的 C.NETnt-Length,每次只發(fā)送很少的字節(jié),讓服務(wù)器一直以為 HTTP 頭部沒有傳輸完成,這樣連接一多就很快會(huì)出現(xiàn)連接耗盡。
應(yīng)用層 DDoS 防御
- 判斷 User-Agent 字段(不可靠,因?yàn)榭梢噪S意構(gòu)造)
- 針對(duì) IP + cookie,限制訪問頻率(由于 cookie 可以更改,IP 可以使用代理,或者肉雞,也不可靠)
- 關(guān)閉服務(wù)器最大連接數(shù)等,合理配置中間件,緩解 DDoS 攻擊。
- 請(qǐng)求中添加驗(yàn)證碼,比如請(qǐng)求中有數(shù)據(jù)庫操作的時(shí)候。
- 編寫代碼時(shí),盡量實(shí)現(xiàn)優(yōu)化,并合理使用緩存技術(shù),減少數(shù)據(jù)庫的讀取操作。
- 加錢堆機(jī)器。。
- 報(bào)警。。
應(yīng)用層的防御有時(shí)比網(wǎng)絡(luò)層的更難,因?yàn)閷?dǎo)致應(yīng)用層被 DDoS 攻擊的因素非常多,有時(shí)往往是因?yàn)槌绦騿T的失誤,導(dǎo)致某個(gè)頁面加載需要消耗大量資源,有時(shí)是因?yàn)橹虚g件配置不當(dāng)?shù)鹊取6鴳?yīng)用層 DDoS 防御的核心就是區(qū)分人與機(jī)器(爬蟲),因?yàn)榇罅康恼?qǐng)求不可能是人為的,肯定是機(jī)器構(gòu)造的。因此如果能有效的區(qū)分人與爬蟲行為,則可以很好地防御此攻擊。
其他 DDoS 攻擊
發(fā)起 DDoS 也是需要大量的帶寬資源的,但是互聯(lián)網(wǎng)就像森林,林子大了什么鳥都有,DDoS 攻擊者也能找到其他的方式發(fā)起廉價(jià)并且極具殺傷力的 DDoS 攻擊。
利用 XSS
舉個(gè)例子,如果 12306 頁面有一個(gè) XSS 持久型漏洞被惡意攻擊者發(fā)現(xiàn),只需在春節(jié)搶票期間在這個(gè)漏洞中執(zhí)行腳本使得往某一個(gè)小站點(diǎn)隨便發(fā)點(diǎn)什么請(qǐng)求,然后隨著用戶訪問的增多,感染用戶增多,被攻擊的站點(diǎn)自然就會(huì)迅速癱瘓了。這種 DDoS 簡直就是無本萬利,不用驚訝,現(xiàn)在大站有 XSS 漏洞的不要太多。
來自 P2P 網(wǎng)絡(luò)攻擊
大家都知道,互聯(lián)網(wǎng)上的 P2P 用戶和流量都是一個(gè)極為龐大的數(shù)字。如果他們都去一個(gè)指定的地方下載數(shù)據(jù),成千上萬的真實(shí) IP 地址連接過來,沒有哪個(gè)設(shè)備能夠支撐住。那 BT 下載來說,偽造一些熱門視頻的種子,發(fā)布到搜索引擎,就足以騙到許多用戶和流量了,但是這只是基礎(chǔ)攻擊。
高級(jí)的 P2P 攻擊,是直接欺騙資源管理服務(wù)器。如迅雷客戶端會(huì)把自己發(fā)現(xiàn)的資源上傳到資源管理服務(wù)器,然后推送給其它需要下載相同資源的用戶,這樣,一個(gè)鏈接就發(fā)布出去。通過協(xié)議逆向,攻擊者偽造出大批量的熱門資源信息通過資源管理中心分發(fā)出去,瞬間就可以傳遍整個(gè) P2P 網(wǎng)絡(luò)。更為恐怖的是,這種攻擊是無法停止的,即使是攻擊者自身也無法停止,攻擊一直持續(xù)到 P2P 官方發(fā)現(xiàn)問題更新服務(wù)器且下載用戶重啟下載軟件為止。
最后總結(jié)下,DDoS 不可能防得住,就好比你的店只能容納 50 人,黑社會(huì)有 100 人,你就換一家大店,能容納 500 人,然后黑社會(huì)又找來了 1000 人,這種堆人頭的做法就是 DDoS 本質(zhì)上的攻防之道,「道高一尺,魔高一丈,魔高一尺,道高一丈」,講真,必要的時(shí)候就答應(yīng)勒索你的人的條件吧,實(shí)在不行就報(bào)警吧。
流量劫持
流量劫持應(yīng)該算是黑產(chǎn)行業(yè)的一大經(jīng)濟(jì)支柱了吧?簡直是讓人惡心到吐,不吐槽了,還是繼續(xù)談干貨吧,流量劫持基本分兩種:DNS 劫持 和 HTTP 劫持,目的都是一樣的,就是當(dāng)用戶訪問網(wǎng)站的時(shí)候,給你展示的并不是或者不完全是 網(wǎng)站 提供的 “內(nèi)容”。
DNS 劫持
DNS 劫持,也叫做域名劫持,可以這么理解,「你打了一輛車想去商場吃飯,結(jié)果你打的車是小作坊派來的,直接給你拉到小作坊去了」,DNS 的作用是把網(wǎng)絡(luò)地址域名對(duì)應(yīng)到真實(shí)的計(jì)算機(jī)能夠識(shí)別的 IP 地址,以便計(jì)算機(jī)能夠進(jìn)一步通信,傳遞網(wǎng)址和內(nèi)容等。如果當(dāng)用戶通過某一個(gè)域名訪問一個(gè)站點(diǎn)的時(shí)候,被篡改的 DNS 服務(wù)器返回的是一個(gè)惡意的釣魚站點(diǎn)的 IP,用戶就被劫持到了惡意釣魚站點(diǎn),然后繼而會(huì)被釣魚輸入各種賬號(hào)密碼信息,泄漏隱私。

這類劫持,要不就是網(wǎng)絡(luò)運(yùn)營商搞的鬼,一般小的網(wǎng)絡(luò)運(yùn)營商與黑產(chǎn)勾結(jié)會(huì)劫持 DNS,要不就是電腦中毒,被惡意篡改了路由器的 DNS 配置,基本上做為開發(fā)者或站長卻是很難察覺的,除非有用戶反饋,現(xiàn)在升級(jí)版的 DNS 劫持還可以對(duì)特定用戶、特定區(qū)域等使用了用戶畫像進(jìn)行篩選用戶劫持的辦法,另外這類廣告顯示更加隨機(jī)更小,一般站長除非用戶投訴否則很難覺察到,就算覺察到了取證舉報(bào)更難。無論如何,如果接到有 DNS 劫持的反饋,一定要做好以下幾件事:
- 取證很重要,時(shí)間、地點(diǎn)、IP、撥號(hào)賬戶、截屏、URL 地址等一定要有。
- 可以跟劫持區(qū)域的電信運(yùn)營商進(jìn)行投訴反饋。
- 如果投訴反饋無效,直接去工信部投訴,一般來說會(huì)加白你的域名。
HTTP 劫持
HTTP 劫持您可以這么理解,「你打了一輛車想去商場吃飯,結(jié)果司機(jī)跟你一路給你遞小作坊的廣告」,HTTP 劫持主要是當(dāng)用戶訪問某個(gè)站點(diǎn)的時(shí)候會(huì)經(jīng)過運(yùn)營商網(wǎng)絡(luò),而不法運(yùn)營商和黑產(chǎn)勾結(jié)能夠截獲 HTTP 請(qǐng)求返回內(nèi)容,并且能夠篡改內(nèi)容,然后再返回給用戶,從而實(shí)現(xiàn)劫持頁面,輕則插入小廣告,重則直接篡改成釣魚網(wǎng)站頁面騙用戶隱私。能夠?qū)嵤┝髁拷俪值母驹颍?HTTP 協(xié)議沒有辦法對(duì)通信對(duì)方的身份進(jìn)行校驗(yàn)以及對(duì)數(shù)據(jù)完整性進(jìn)行校驗(yàn)。如果能解決這個(gè)問題,則流量劫持將無法輕易發(fā)生。所以防止 HTTP 劫持的方法只有將內(nèi)容加密,讓劫持者無法破解篡改,這樣就可以防止 HTTP 劫持了。
HTTPS 協(xié)議就是一種基于 SSL 協(xié)議的安全加密網(wǎng)絡(luò)應(yīng)用層協(xié)議,可以很好的防止 HTTP 劫持。這里有篇 文章 講的不錯(cuò)。HTTPS 在這就不深講了,后面有機(jī)會(huì)我會(huì)單獨(dú)好好講講 HTTPS。如果不想站點(diǎn)被 HTTP 劫持,趕緊將你的站點(diǎn)全站改造成 HTTPS 吧。
服務(wù)器漏洞
服務(wù)器除了以上提到的那些大名鼎鼎的漏洞和臭名昭著的攻擊以外,其實(shí)還有很多其他的漏洞,往往也很容易被忽視,在這個(gè)小節(jié)也稍微介紹幾種。
越權(quán)操作漏洞
如果你的系統(tǒng)是有登錄控制的,那就要格外小心了,因?yàn)楹苡锌赡苣愕南到y(tǒng)越權(quán)操作漏洞,越權(quán)操作漏洞可以簡單的總結(jié)為 「A 用戶能看到或者操作 B 用戶的隱私內(nèi)容」,如果你的系統(tǒng)中還有權(quán)限控制就更加需要小心了。所以每一個(gè)請(qǐng)求都需要做 userid 的判斷
以下是一段有漏洞的后端示意代碼:
|
|
以上代碼是任何人都可以查詢到任何用戶的消息,只要有 msg_id 就可以,這就是比較典型的越權(quán)漏洞,需要如下這么改進(jìn)一下:
|
|
嗯,大概就是這個(gè)意思,如果有更嚴(yán)格的權(quán)限控制,那在每個(gè)請(qǐng)求中凡是涉及到數(shù)據(jù)庫的操作都需要先進(jìn)行嚴(yán)格的驗(yàn)證,并且在設(shè)計(jì)數(shù)據(jù)庫表的時(shí)候需要考慮進(jìn) userId 的賬號(hào)關(guān)聯(lián)以及權(quán)限關(guān)聯(lián)。
目錄遍歷漏洞
目錄遍歷漏洞指通過在 URL 或參數(shù)中構(gòu)造 ../,./ 和類似的跨父目錄字符串的 ASCII 編碼、unicode 編碼等,完成目錄跳轉(zhuǎn),讀取操作系統(tǒng)各個(gè)目錄下的敏感文件,也可以稱作「任意文件讀取漏洞」。
目錄遍歷漏洞原理:程序沒有充分過濾用戶輸入的 ../ 之類的目錄跳轉(zhuǎn)符,導(dǎo)致用戶可以通過提交目錄跳轉(zhuǎn)來遍歷服務(wù)器上的任意文件。使用多個(gè).. 符號(hào),不斷向上跳轉(zhuǎn),最終停留在根 /,通過絕對(duì)路徑去讀取任意文件。
目錄遍歷漏洞幾個(gè)示例和測試,一般構(gòu)造 URL 然后使用瀏覽器直接訪問,或者使用 Web 漏洞掃描工具檢測,當(dāng)然也可以自寫程序測試。
|
|
防御 方法就是需要對(duì) URL 或者參數(shù)進(jìn)行 ../,./ 等字符的轉(zhuǎn)義過濾。
物理路徑泄漏
物理路徑泄露屬于低風(fēng)險(xiǎn)等級(jí)缺陷,它的危害一般被描述為「攻擊者可以利用此漏洞得到信息,來對(duì)系統(tǒng)進(jìn)一步地攻擊」,通常都是系統(tǒng)報(bào)錯(cuò) 500 的錯(cuò)誤信息直接返回到頁面可見導(dǎo)致的漏洞。得到物理路徑有些時(shí)候它能給攻擊者帶來一些有用的信息,比如說:可以大致了解系統(tǒng)的文件目錄結(jié)構(gòu);可以看出系統(tǒng)所使用的第三方軟件;也說不定會(huì)得到一個(gè)合法的用戶名(因?yàn)楹芏嗳税炎约旱挠脩裘鳛榫W(wǎng)站的目錄名)。
防止這種泄漏的方法就是做好后端程序的出錯(cuò)處理,定制特殊的 500 報(bào)錯(cuò)頁面。
源碼暴露漏洞
和物理路徑泄露類似,就是攻擊者可以通過請(qǐng)求直接獲取到你站點(diǎn)的后端源代碼,然后就可以對(duì)系統(tǒng)進(jìn)一步研究攻擊。那么導(dǎo)致源代碼暴露的原因是什么呢?基本上就是發(fā)生在服務(wù)器配置上了,服務(wù)器可以設(shè)置哪些路徑的文件才可以被直接訪問的,這里給一個(gè) koa 服務(wù)器的例子,正常的 koa 服務(wù)器可以通過 koa-static 中間件去指定靜態(tài)資源的目錄,好讓靜態(tài)資源可以通過路徑的路由訪問。比如你的系統(tǒng)源代碼目錄是這樣的:
|
|
你想要將 static 的文件夾配成靜態(tài)資源目錄,你應(yīng)該會(huì)在 server.js 做如下配置:
|
|
但是如果配錯(cuò)了靜態(tài)資源的目錄,可能就出大事了,比如:
|
|
這樣所有的源代碼都可以通過路由訪問到了,所有的服務(wù)器都提供了靜態(tài)資源機(jī)制,所以在通過服務(wù)器配置靜態(tài)資源目錄和路徑的時(shí)候,一定要注意檢驗(yàn),不然很可能產(chǎn)生漏洞。
最后,希望 Web 開發(fā)者們能夠管理好自己的代碼隱私,注意代碼安全問題,比如不要將產(chǎn)品的含有敏感信息的代碼放到第三方外部站點(diǎn)或者暴露給外部用戶,尤其是前端代碼,私鑰類似的保密性的東西不要直接輸出在代碼里或者頁面中。也許還有很多值得注意的點(diǎn),但是歸根結(jié)底還是繃住安全那根弦,對(duì)待每一行代碼都要多多推敲。