安全攻防五:XSS,當(dāng)你“被發(fā)送”了一條微博時,到底發(fā)生了什么

在前面的文章中,我們重點講解了安全的一些基礎(chǔ)知識,更多地是從宏觀的層面上來談?wù)摪踩?。但安全不是一個靠宏觀指導(dǎo)就能夠落地的東西。因此,接下來會結(jié)合真實案例中的各種安全問題,來介紹具體的安全防護(hù)手段和工具。今天,我們就先從最基礎(chǔ)的 Web 安全開始。

在 Web 安全這個模塊中,我們所談?wù)摰?Web,是指所有基于 HTTP 或者其他超文本傳輸協(xié)議(RPC 等)開發(fā)的應(yīng)用,包括:網(wǎng)頁、App、API 接口等等。這類應(yīng)用的共同點是:通過 HTTP 等文本協(xié)議,在客戶端和服務(wù)端之間進(jìn)行數(shù)據(jù)交換??蛻舳诵枰獙⒎?wù)端傳出的數(shù)據(jù)展示渲染出來,服務(wù)端需要將客戶端傳入的數(shù)據(jù)進(jìn)行對應(yīng)的處理。而 Web 安全所涉及的正是這些應(yīng)用中存在的各類安全問題。

說一個真實案例,某一天,微博公司的網(wǎng)頁應(yīng)用中發(fā)生了一件事。有很多用戶發(fā)送了同樣類型的內(nèi)容,而且這些內(nèi)容都是一個帶有誘惑性的問題和一個可以點擊的鏈接。這些用戶全部反饋說,這不是他們自己發(fā)的。前端開發(fā)表示,用戶內(nèi)容都是后端產(chǎn)生的,他不負(fù)責(zé)。后端開發(fā)表示,這些內(nèi)容都是用戶自己提交上來的,他也不負(fù)責(zé)。

整個事件的核心問題,其實出在這個可以點擊的鏈接上。在這個事件中,黑客并不需要入侵到微博服務(wù)器中,只要用戶點擊了這個鏈接,就會“被發(fā)送”這樣的博文。這就是著名的 XSS 攻擊所能夠?qū)崿F(xiàn)的效果。那么,XSS 攻擊究竟是怎么產(chǎn)生的呢?我們究竟該如何防護(hù)呢?

XSS 攻擊是如何產(chǎn)生的

首先,我們來看,XSS 攻擊是如何產(chǎn)生的。作為最普遍的網(wǎng)頁語言,HTML 非常靈活,你可以在任意時候?qū)?HTML 進(jìn)行修改。但是,這種靈活性也給了黑客可趁之機(jī):通過給定異常的輸入,黑客可以在你的瀏覽器中,插入一段惡意的 JavaScript 腳本,從而竊取你的隱私信息或者仿冒你進(jìn)行操作。這就是 XSS 攻擊(Cross-Site Scripting,跨站腳本攻擊)的原理。

你現(xiàn)在應(yīng)該對 XSS 有了一個大致的了解,除此之外,你還需要了解三種 XSS 攻擊,它們分別是:反射型 XSS、基于 DOM 的 XSS 以及持久型 XSS。

  • 1.反射型 XSS

假設(shè)現(xiàn)在有一個搜索網(wǎng)頁,當(dāng)你輸入任意一個關(guān)鍵詞,并點擊“搜索”按鈕之后,這個網(wǎng)頁就會給你展示“你搜索的結(jié)果內(nèi)容是:XXX”。

反射xss搜索

我們以 PHP 為例,這個網(wǎng)頁的服務(wù)端實現(xiàn)邏輯如下所示:

<!DOCTYPE html>
<html>
  <body>
    <form role="search" action="" method="GET">
      <input type="text" name="search" placeholder="請輸入要搜索的內(nèi)容">
          <button type="submit">搜索</button>
      </form>
    <?php
      if (isset($_GET['search']) && !empty($_GET['search'])) {
        $search = $_GET['search'];
        echo "<h3>你搜索的結(jié)果內(nèi)容是:" . $search . "</h3>";
      }
    ?>
  </body>
</html>

我們可以看到,這段代碼的邏輯是將搜索框輸入的內(nèi)容,拼接成字符串,然后填充到最終的 HTML 中。而且這個過程中沒有任何的過濾措施,如果黑客想要對這個過程發(fā)起攻擊,他會輸入下面這行代碼:

</h3><script>alert('xss');</script><h3>

黑客輸入這段字符后,網(wǎng)頁會彈出一個告警框(我自己測試的時候,發(fā)現(xiàn)部分瀏覽器,如 Safari 不會彈出告警框,這是因為瀏覽器自身提供了一定的 XSS 保護(hù)功能)。

XSS告警

通過查看網(wǎng)頁的源碼,可以發(fā)現(xiàn),這中間多了一段 JavaScript 的腳本:

XSS網(wǎng)頁源代碼

這就是我們所說的反射型 XSS 攻擊的過程。其實它攻擊的原理很簡單。我們可以總結(jié)一下,即通過開頭的</h3>和結(jié)尾的<h3>,將原本的<h3>標(biāo)簽進(jìn)行閉合,然后中間通過<script>標(biāo)簽插入 JavaScript 代碼并執(zhí)行,就完成了整個反射型 XSS 的流程。

你可以注意一下瀏覽器的地址:http://localhost/index.php?search=<%2Fh3><script>alert('xss')%3B<%2Fscript><h3>。實際上,任何人只要點擊了這個鏈接,就會執(zhí)行一段黑客定義的 JavaScript 腳本。所以,我們經(jīng)常說,不要點擊任何未知的鏈接。

看下面這張圖, 黑客誘導(dǎo)你點擊了某個鏈接,這個鏈接提供的服務(wù),可能就是上述的搜索功能。網(wǎng)頁在解析到鏈接的參數(shù)后,執(zhí)行正常的搜索邏輯,但是因為漏洞,網(wǎng)頁中被填入了黑客定義的腳本。使得用戶的瀏覽器,最終執(zhí)行的是黑客的腳本。

XSS攻擊流程圖
  • 2.基于 DOM 的 XSS

在上面的例子中我們可以看到,反射型 XSS 產(chǎn)生在前后端一體的網(wǎng)頁應(yīng)用中,服務(wù)端邏輯會改變最終的網(wǎng)頁代碼。但是,目前更流行的其實是前后端分離,這樣網(wǎng)頁的代碼不會受服務(wù)端影響。那么,這樣是不是就安全了呢?

顯然不是的。盡管服務(wù)端無法改變網(wǎng)頁代碼,但網(wǎng)頁本身的 JavaScript 仍然可以改變。而黑客只要利用了這一點,同樣能夠在網(wǎng)頁中插入自己的腳本。這也就是所謂的基于 DOM 的 XSS 漏洞。對于上述搜索功能,通過前后端分離,它的源碼就變成了下面這樣:

<!DOCTYPE html>
<html>
  <body>
    <form role="search" action="" method="GET">
      <input type="text" name="search" placeholder="請輸入要搜索的內(nèi)容">
          <button type="submit">搜索</button>
      </form>
      <script>
        var search = location.search.substring(8);
        document.write('你搜索的結(jié)果內(nèi)容是:' + decodeURIComponent(search));
      </script>
  </body>
</html>

這段代碼能夠?qū)崿F(xiàn)和之前的 PHP 代碼相同的邏輯:當(dāng)你在搜索框點擊搜索關(guān)鍵詞之后,網(wǎng)頁會展示你輸入的關(guān)鍵詞。只不過,HTML 是通過 JavaScript 腳本修改DOM來實現(xiàn)這個功能的。

那么和上述例子一樣,在基于 DOM 的 XSS 中,黑客也可以通過插入一段<script>alert('xss');</script>來執(zhí)行指定的 JavaScript 腳本?;?DOM 的 XSS 總體流程如下圖所示??梢钥吹?,這個流程其實和反射型 XSS 一致,只是不需要經(jīng)過服務(wù)端了而已。

基于DOM的XSS流程圖
  • 3.持久型 XSS

你可以回想一下,當(dāng)你在網(wǎng)頁中搜索一個關(guān)鍵詞時,實際上與這個關(guān)鍵詞相關(guān)的所有搜索結(jié)果都會被展示出來。一旦這些搜索結(jié)果中,包含黑客提供的某個惡意 JavaScript 腳本,那么只要我們?yōu)g覽了這個網(wǎng)頁,就有可能會執(zhí)行這些腳本。這就是持久型 XSS。因為這些惡意的搜索結(jié)果,會長期保存在服務(wù)端數(shù)據(jù)庫中,所以它又叫作存儲型 XSS。在應(yīng)用中,存儲用戶的輸入并對它們進(jìn)行展示的地方,都可能出現(xiàn)持久型 XSS。比如:搜索結(jié)果、評論、博文等等。

有了前面的鋪墊,持久型 XSS 的產(chǎn)生過程就很好理解了,具體我就不細(xì)說了,我還是把總體流程畫了一張圖,你可以仔細(xì)看看。

持久性XSS流程圖

相比前面兩種 XSS 攻擊來說,持久型 XSS 往往具備更強(qiáng)的危害性。因為對于一個反射型或者基于 DOM 的 XSS 來說,需要黑客誘導(dǎo)用戶點擊惡意的 URL,才能夠成功地在用戶瀏覽器上執(zhí)行 JavaScript 腳本。這對黑客在誘導(dǎo)用戶操作方面的能力提出了考驗:并不是所有的用戶都是小白,一些有經(jīng)驗的用戶會在點擊鏈接前進(jìn)行一定的考慮。

而持久型 XSS 則不同,它是將惡意的 JavaScript 腳本寫入到了正常的服務(wù)端數(shù)據(jù)庫中,因此,只要用戶正常的使用業(yè)務(wù)功能,就會被注入 JavaScript 腳本。所以說,持久型 XSS 在傳播速度和傳播范圍上,會遠(yuǎn)遠(yuǎn)超出其他類型的 XSS。

通過 XSS 攻擊,黑客能做什么

這 3 種 XSS 攻擊,都是因為黑客在用戶的瀏覽器中執(zhí)行了惡意的 JavaScript 腳本。那么執(zhí)行這些 JavaScript 腳本有什么樣的危害呢?我把這些危害總結(jié)了一下,可以分為下面幾種。

1.竊取 Cookie

從上面的例子中,我們可以看到,黑客可以竊取用戶的 Cookie。因為黑客注入的 JavaScript 代碼是運行在 server.com 這個域名下的,因此,黑客可以在 JavaScript 中通過 document.cookie 獲得 Cookie 信息。

另外,需要我們注意的是,受SOP(Same Origin Policy,同源策略)保護(hù),我們在 server.com 中是無法直接向 hacker.com 發(fā)送 GET 或者 POST 請求的。這也是為什么,在上面的例子中,我們需要通過 window.location 來執(zhí)行跳轉(zhuǎn)操作,間接地將 Cookie 信息發(fā)送出去。除了 window.location 之外,我們還可以通過加載 JavaScript 文件、圖片等方式,向 attacker.com 發(fā)送帶有 Cookie 的 GET 請求。

2.未授權(quán)操作

除了竊取敏感信息以外,黑客還可以利用 JavaScript 的特性,直接代替用戶在 HTML 進(jìn)行各類操作。

在文章開頭,我們提到的微博 XSS 攻擊事件中,黑客就利用 JavaScript 腳本,讓用戶發(fā)送了一個微博,微博中同時還帶有反射型 XSS 的鏈接。這樣一來,每個點擊鏈接的用戶都會通過微博的形式,誘導(dǎo)更多的用戶點擊鏈接,一傳十、十傳百,造成大范圍的傳播。

3.按鍵記錄和釣魚

JavaScript 的功能十分強(qiáng)大,它還能夠記錄用戶在瀏覽器中的大部分操作。比如:鼠標(biāo)的軌跡、鍵盤輸入的信息等。也就是說,你輸入的賬號名和密碼,都可以被 JavaScript 記錄下來,從而被黑客獲取到。

另外,即使某個存在 XSS 漏洞的頁面不具備任何輸入框,黑客還可以通過修改 DOM,偽造一個登錄框,來誘導(dǎo)用戶在本不需要登錄的頁面,去輸入自己的用戶名和密碼。這也是“釣魚”的一種形式,在這個過程中用戶訪問的域名是完全正常的,只是頁面被篡改了,所以具備更高的迷惑性。

如何進(jìn)行 XSS 防護(hù)

  • 驗證輸入 OR 驗證輸出

防護(hù)的核心原則是:一切用戶輸入皆不可信。你的第一反應(yīng)一定是,這很好實現(xiàn)啊,當(dāng)接收到用戶的輸入時,我們就進(jìn)行驗證,這不就做到了嗎?實際上并不是這么簡單的,我們還是通過搜索這個例子來看。在用戶點擊“搜索”按鈕之后,如果我們馬上對他輸入的內(nèi)容進(jìn)行驗證,這樣就會產(chǎn)生兩個問題。

  1. 你將無法保存用戶的原始輸入信息。這樣一來,當(dāng)出現(xiàn)了 Bug 或者想要對黑客行為進(jìn)行溯源時,你只能“推斷”,而不能準(zhǔn)確地獲取用戶的原始輸入。

  2. 用戶的內(nèi)容可能會被多種語言獲取和使用,提前編碼或者處理,將產(chǎn)生未知的問題。比如,在舊版本的 PHP 中,就存在“magic quotes”的漏洞,因為 PHP 無法處理某些編碼的字符而導(dǎo)致崩潰。

因此,我更推薦在需要輸出的時候去進(jìn)行驗證,即當(dāng)需要展示的時候,我們再對內(nèi)容進(jìn)行驗證,這樣我們就能夠根據(jù)不同的環(huán)境去采取不同的保護(hù)方案了。在 HTML 中,常見的 XSS 注入點我已經(jīng)總結(jié)好了,你可以看下面這個表格:

常見XSS注入點
  • 編碼

XSS 防護(hù)的核心原則就是驗證,那具體該怎么去做驗證呢?我認(rèn)為,我們可以優(yōu)先采用編碼的方式來完成。所謂編碼,就是將部分瀏覽器識別的關(guān)鍵詞進(jìn)行轉(zhuǎn)換(比如 < 和 >),從而避免瀏覽器產(chǎn)生誤解。對于客戶端來說,編碼意味著,使用 JavaScript 提供的功能對用戶內(nèi)容進(jìn)行處理。具體的方法我也總結(jié)了一下,你可以看這個表格。

XSS編碼

對于最后一個注入點,即在 JavaScript 中進(jìn)行注入,目前還沒有內(nèi)置的編碼方式來對它提供保護(hù)。你當(dāng)然可以通過諸如 URL 編碼等方式進(jìn)行編碼,但這有可能對應(yīng)用的自身邏輯產(chǎn)生影響。因此,JavaScript 中的注入并不適合通過編碼來進(jìn)行保護(hù)。

  • 檢測和過濾

但是,在很多時候,編碼會對網(wǎng)頁實際的展現(xiàn)效果產(chǎn)生影響。比如,原本用戶可能想展示一個 1>0,卻被編碼展示成了 1&gt0。盡管網(wǎng)絡(luò)環(huán)境安全了,卻對用戶造成了困擾。那么,我們還可以采取哪些方法進(jìn)行驗證呢?接下來我就為你介紹一下檢測和過濾。

首先,我們需要對用戶的內(nèi)容進(jìn)行檢測。在這里,我們可以采用黑名單和白名單的規(guī)則。黑名單往往是我們最直接想到的方法:既然黑客要插入<javascript>標(biāo)簽,那么我們就檢測用戶內(nèi)容中是否存在<javascript>標(biāo)簽就好了。

但是,黑客的攻擊方法是無窮無盡的。你檢測了<javascript>,黑客就可以改成<JavaScript>(因為 HTML 標(biāo)簽對大小寫不敏感),甚至有些時候還能夠編碼成&#106;avascript等等。另外,HTML5 的發(fā)展速度很快,總是有新的標(biāo)簽被開發(fā)出來,這些新標(biāo)簽中也可能包含新的注入點。因此,黑名單的更新和維護(hù)過程,是需要我們和黑客進(jìn)行長期對抗的過程

所以,在檢測中,我更推薦使用白名單的規(guī)則。因為白名單的規(guī)則比較簡單,并且十分有效。比如,在只輸入一個分?jǐn)?shù)的地方,規(guī)定只有整型變量是合法的。這樣一來,你就能夠檢測出 99.99% 的攻擊行為了。

說完了檢測,那當(dāng)發(fā)現(xiàn)某個用戶的內(nèi)容可能存在 XSS 攻擊腳本時,我們該怎么處理呢?這個時候,處理選項有兩個:拒絕或者過濾。毫無疑問,拒絕是最安全的選項。一旦你發(fā)現(xiàn)可能的 XSS 攻擊腳本,只要不將這段用戶內(nèi)容展現(xiàn)出來,就能避免可能的攻擊行為。

但是,拒絕會阻礙用戶的使用流程,從用戶體驗的角度上來考慮的話,過濾會更被用戶所接受。上面提到的編碼就屬于一種過濾的方式。除此之外,我們也可以直接對敏感字符進(jìn)行替換刪除等。需要注意的是,在替換的時候,一定不能采取黑名單的形式(比如:將 javascript 進(jìn)行刪除,那黑客就可以通過 JavaScript 來繞過),而是應(yīng)該采取白名單的形式(比如,除了 div 之外的標(biāo)簽全部刪除)。

同樣地,過濾的流程也必須徹底。比如,我看到過有人采用下面這行字符串來過濾 javascript 標(biāo)簽:

$str=str_replace('<javascript>','',$str);

但黑客只需要將 str 的值變成<java<javascript>script>就可以了,因為str_replace('<javascript>','','<java<javascript>script>')的結(jié)果就是<javascript>。

  • CSP

面對 XSS 這樣一個很普遍的問題,W3C 提出了 CSP(Content Security Policy,內(nèi)容安全策略)來提升 Web 的安全性。所謂 CSP,就是在服務(wù)端返回的 HTTP header 里面添加一個 Content-Security-Policy 選項,然后定義資源的白名單域名。瀏覽器就會識別這個字段,并限制對非白名單資源的訪問。配置樣例如下所示:

Content-Security-Policy:default-src ‘none’; script-src ‘self’; 
connect-src ‘self’; img-src ‘self’; style-src ‘self’;

那我們?yōu)槭裁匆拗仆庥蛸Y源的訪問呢?這是因為 XSS 通常會受到長度的限制,導(dǎo)致黑客無法提交一段完整的 JavaScript 代碼。為了解決這個問題,黑客會采取引用一個外域 JavaScript 資源的方式來進(jìn)行注入。除此之外,限制了外域資源的訪問,也就限制了黑客通過資源請求的方式,繞過 SOP 發(fā)送 GET 請求。目前,CSP 還是受到了大部分瀏覽器支持的,只要用戶使用的是最新的瀏覽器,基本都能夠得到很好的保護(hù)。

總結(jié)

簡單來說,XSS 就是利用 Web 漏洞,在用戶的瀏覽器中執(zhí)行黑客定義的 JavaScript 腳本,這樣一種攻擊方式。根據(jù)攻擊方式的不同,可以分為:反射型 XSS、基于 DOM 的 XSS 和持久型 XSS。通過在用戶的瀏覽器中注入腳本,黑客可以通過各種方式,采集到用戶的敏感信息,包括:Cookie、按鍵記錄、密碼等。

預(yù)防 XSS 主要通過對用戶內(nèi)容的驗證來完成。首先,我推薦在需要展示用戶內(nèi)容的時候去進(jìn)行驗證,而不是當(dāng)用戶輸入的時候就去驗證。在驗證過程中,我們優(yōu)先采用編碼的方式來完成。如果編碼影響到了業(yè)務(wù)的正常功能,我們就可以采用白名單的檢測和過濾方式來進(jìn)行驗證。除此之外,我們可以根據(jù)業(yè)務(wù)需要,配置合適的 CSP 規(guī)則,這也能在很大程度上降低 XSS 產(chǎn)生的影響。

另外,在這里,我把本文的重點內(nèi)容梳理了一個知識圖。你可以根據(jù)它來查漏補(bǔ)缺,加深印象。

腦圖

未完待續(xù)
更多文章請關(guān)注我的個人訂閱號,謝謝!

在這里插入圖片描述

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • XSS攻擊 什么是 XSS Cross-Site Scripting (跨站腳本攻擊)簡稱XSS,是一種代碼注入攻...
    CodeMT閱讀 1,420評論 0 4
  • 昨天吃飯時女兒和我聊起她的很多第一次,第一次我教她系紐扣,第一次教她寫數(shù)字5,第一次教她擺衣架,還有兩個人在床上讀...
    Suere貓閱讀 212評論 0 0
  • 緣起 書應(yīng)該是過年前(大概1月左右)從雨花圖書館借來的,差不多2月份的計劃4本書已經(jīng)讀完。 20190224開始看...
    im天行閱讀 761評論 0 0
  • “阿姨,我先進(jìn)去,有什么問題我叫您?!崩钽宀缓米尠⒁堂半U,壯著膽子打開了門。 做好了一戰(zhàn)的準(zhǔn)備,李沐腦子一熱沖進(jìn)來...
    墨微閱讀 403評論 1 0
  • 夢見牙齒移位,右門牙右邊的牙齒凹進(jìn)去,我嘗試掰上去,結(jié)果啪的一聲,松動了,非常輕松的拿下來。我發(fā)現(xiàn)我的門牙已經(jīng)偏離...
    天才白癡錢錢錢閱讀 219評論 0 0

友情鏈接更多精彩內(nèi)容