1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        【React】826- 淺談 React 中的 XSS 攻擊

        共 5683字,需瀏覽 12分鐘

         ·

        2021-01-06 01:46

        ???這是第?70?篇不摻水的原創(chuàng),想要了解更多,請戳上方藍(lán)色字體:政采云前端團(tuán)隊(duì)?關(guān)注我們吧~

        本文首發(fā)于政采云前端團(tuán)隊(duì)博客:淺談 React 中的 XSS 攻擊

        https://www.zoo.team/article/xss-in-react


        前言

        前端一般會(huì)面臨 XSS 這樣的安全風(fēng)險(xiǎn),但隨著 React 等現(xiàn)代前端框架的流行,使我們在平時(shí)開發(fā)時(shí)不用太關(guān)注安全問題。以 React 為例,React 從設(shè)計(jì)層面上就具備了很好的防御 XSS 的能力。本文將以源碼角度,看看 React 做了哪些事情來實(shí)現(xiàn)這種安全性的。

        XSS 攻擊是什么

        Cross-Site Scripting(跨站腳本攻擊)簡稱 XSS,是一種代碼注入攻擊。XSS 攻擊通常指的是利用網(wǎng)頁的漏洞,攻擊者通過巧妙的方法注入 XSS 代碼到網(wǎng)頁,因?yàn)闉g覽器無法分辨哪些腳本是可信的,導(dǎo)致 XSS 腳本被執(zhí)行。XSS 腳本通常能夠竊取用戶數(shù)據(jù)并發(fā)送到攻擊者的網(wǎng)站,或者冒充用戶,調(diào)用目標(biāo)網(wǎng)站接口并執(zhí)行攻擊者指定的操作。

        XSS 攻擊類型

        反射型 XSS

        • XSS 腳本來自當(dāng)前 HTTP 請求
        • 當(dāng)服務(wù)器在 HTTP 請求中接收數(shù)據(jù)并將該數(shù)據(jù)拼接在 HTML 中返回時(shí),例子:
        //?某網(wǎng)站具有搜索功能,該功能通過 URL 參數(shù)接收用戶提供的搜索詞:
        https://xxx.com/search?query=123
        //?服務(wù)器在對此 URL 的響應(yīng)中回顯提供的搜索詞:
        <p>您搜索的是:?123p>
        //?如果服務(wù)器不對數(shù)據(jù)進(jìn)行轉(zhuǎn)義等處理,則攻擊者可以構(gòu)造如下鏈接進(jìn)行攻擊:
        https://xxx.com/search?query=
        //?該 URL 將導(dǎo)致以下響應(yīng),并運(yùn)行 alert('xss'):
        <p>您搜索的是:?<img?src=""?onerror?="alert('xss')">p>
        //?如果有用戶請求攻擊者的 URL ,則攻擊者提供的腳本將在用戶的瀏覽器中執(zhí)行。

        存儲(chǔ)型 XSS

        • XSS 腳本來自服務(wù)器數(shù)據(jù)庫中
        • 攻擊者將惡意代碼提交到目標(biāo)網(wǎng)站的數(shù)據(jù)庫中,普通用戶訪問網(wǎng)站時(shí)服務(wù)器將惡意代碼返回,瀏覽器默認(rèn)執(zhí)行,例子:
        //?某個(gè)評論頁,能查看用戶評論。
        //?攻擊者將惡意代碼當(dāng)做評論提交,服務(wù)器沒對數(shù)據(jù)進(jìn)行轉(zhuǎn)義等處理
        //?評論輸入:
        <textarea>
        ??<img?src=""?onerror?="alert('xss')">
        textarea>
        //?則攻擊者提供的腳本將在所有訪問該評論頁的用戶瀏覽器執(zhí)行

        DOM 型 XSS

        該漏洞存在于客戶端代碼,與服務(wù)器無關(guān)

        • 類似反射型,區(qū)別在于 DOM 型 XSS 并不會(huì)和后臺(tái)進(jìn)行交互,前端直接將 URL 中的數(shù)據(jù)不做處理并動(dòng)態(tài)插入到 HTML 中,是純粹的前端安全問題,要做防御也只能在客戶端上進(jìn)行防御。

        React 如何防止 XSS 攻擊

        無論使用哪種攻擊方式,其本質(zhì)就是將惡意代碼注入到應(yīng)用中,瀏覽器去默認(rèn)執(zhí)行。React 官方中提到了 React DOM 在渲染所有輸入內(nèi)容之前,默認(rèn)會(huì)進(jìn)行轉(zhuǎn)義。它可以確保在你的應(yīng)用中,永遠(yuǎn)不會(huì)注入那些并非自己明確編寫的內(nèi)容。所有的內(nèi)容在渲染之前都被轉(zhuǎn)換成了字符串,因此惡意代碼無法成功注入,從而有效地防止了 XSS 攻擊。我們具體看下:

        自動(dòng)轉(zhuǎn)義

        React 在渲染 HTML 內(nèi)容和渲染 DOM 屬性時(shí)都會(huì)將 "'&<> 這幾個(gè)字符進(jìn)行轉(zhuǎn)義,轉(zhuǎn)義部分源碼如下:

        for?(index?=?match.index;?index???switch?(str.charCodeAt(index))?{
        ????case?34:?//?"
        ??????escape?=?'"';
        ??????break;
        ????case?38:?//?&
        ??????escape?=?'&';
        ??????break;
        ????case?39:?//?'
        ??????escape?=?''';
        ??????break;
        ????case?60:?//?<
        ??????escape?=?'<';
        ??????break;
        ????case?62:?//?>
        ??????escape?=?'>';
        ??????break;
        ????default:
        ??????continue;
        ????}
        ??}

        這段代碼是 React 在渲染到瀏覽器前進(jìn)行的轉(zhuǎn)義,可以看到對瀏覽器有特殊含義的字符都被轉(zhuǎn)義了,惡意代碼在渲染到 HTML 前都被轉(zhuǎn)成了字符串,如下:

        //?一段惡意代碼
        ""?onerror?="alert('xss')">?
        //?轉(zhuǎn)義后輸出到?html?中
        ?

        這樣就有效的防止了 XSS 攻擊。

        JSX 語法

        JSX 實(shí)際上是一種語法糖,Babel 會(huì)把 JSX 編譯成 React.createElement() 的函數(shù)調(diào)用,最終返回一個(gè) ReactElement,以下為這幾個(gè)步驟對應(yīng)的代碼:

        //?JSX
        const?element?=?(
        ??<h1?className="greeting">
        ????Hello,?world!
        ??h1>

        );
        //?通過?babel?編譯后的代碼
        const?element?=?React.createElement(
        ??'h1',
        ??{className:?'greeting'},
        ??'Hello,?world!'
        );
        //?React.createElement()?方法返回的?ReactElement
        const?element?=?{
        ??$$typeof:?Symbol('react.element'),
        ??type:?'h1',
        ??key:?null,
        ??props:?{
        ????children:?'Hello,?world!',
        ??????className:?'greeting'???
        ??}
        ??...
        }

        我們可以看到,最終渲染的內(nèi)容是在 Children 屬性中,那了解了 JSX 的原理后,我們來試試能否通過構(gòu)造特殊的 Children 進(jìn)行 XSS 注入,來看下面一段代碼:

        const?storedData?=?`{
        ??"ref":null,
        ??"type":"body",
        ??"props":{
        ??"dangerouslySetInnerHTML":{
        ??"__html":""
        ??????}
        ??}
        }`
        ;
        //?轉(zhuǎn)成?JSON
        const?parsedData?=?JSON.parse(storedData);
        //?將數(shù)據(jù)渲染到頁面
        render?()?{
        ??return?<span>?{parsedData}?span>;?
        }

        這段代碼中, 運(yùn)行后會(huì)報(bào)以下錯(cuò)誤,提示不是有效的 ReactChild

        Uncaught?(in?promise)?Error:?Objects?are?not?valid?as?a?React?child?(found:?object?with?keys?{ref,?type,?props}).?If?you?meant?to?render?a?collection?of?children,?use?an?array?instead.

        那究竟是哪里出問題了?我們看一下 ReactElement 的源碼:

        const?symbolFor?=?Symbol.for;
        REACT_ELEMENT_TYPE?=?symbolFor('react.element');
        const?ReactElement?=?function(type,?key,?ref,?self,?source,?owner,?props)?{
        ??const?element?=?{
        ????//?這個(gè)?tag?唯一標(biāo)識了此為?ReactElement
        ????$$typeof:?REACT_ELEMENT_TYPE,
        ????//?元素的內(nèi)置屬性
        ????type:?type,
        ????key:?key,
        ????ref:?ref,
        ????props:?props,
        ????//?記錄創(chuàng)建此元素的組件
        ????_owner:?owner,
        ??};
        ??...
        ??return?element;
        }

        注意到其中有個(gè)屬性是 $$typeof,它是用來標(biāo)記此對象是一個(gè) ReactElement,React 在進(jìn)行渲染前會(huì)通過此屬性進(jìn)行校驗(yàn),校驗(yàn)不通過將會(huì)拋出上面的錯(cuò)誤。React 利用這個(gè)屬性來防止通過構(gòu)造特殊的 Children 來進(jìn)行的 XSS 攻擊,原因是 $$typeof 是個(gè) Symbol 類型,進(jìn)行 JSON 轉(zhuǎn)換后會(huì) Symbol 值會(huì)丟失,無法在前后端進(jìn)行傳輸。如果用戶提交了特殊的 Children,也無法進(jìn)行渲染,利用此特性,可以防止存儲(chǔ)型的 XSS 攻擊。

        在 React 中可引起漏洞的一些寫法

        使用 dangerouslySetInnerHTML

        dangerouslySetInnerHTML 是 React 為瀏覽器 DOM 提供 innerHTML 的替換方案。通常來講,使用代碼直接設(shè)置 HTML 存在風(fēng)險(xiǎn),因?yàn)楹苋菀资褂脩舯┞对?XSS 攻擊下,因?yàn)楫?dāng)使用 dangerouslySetInnerHTML 時(shí),React 將不會(huì)對輸入進(jìn)行任何處理并直接渲染到 HTML 中,如果攻擊者在 dangerouslySetInnerHTML 傳入了惡意代碼,那么瀏覽器將會(huì)運(yùn)行惡意代碼??聪略创a:

        function?getNonChildrenInnerMarkup(props)?{
        ??const?innerHTML?=?props.dangerouslySetInnerHTML;?//?有dangerouslySetInnerHTML屬性,會(huì)不經(jīng)轉(zhuǎn)義就渲染__html的內(nèi)容
        ??if?(innerHTML?!=?null)?{
        ????if?(innerHTML.__html?!=?null)?{
        ??????return?innerHTML.__html;
        ????}
        ??}?else?{
        ????const?content?=?props.children;
        ????if?(typeof?content?===?'string'?||?typeof?content?===?'number')?{
        ??????return?escapeTextForBrowser(content);
        ????}
        ??}
        ??return?null;
        }

        所以平時(shí)開發(fā)時(shí)最好避免使用 dangerouslySetInnerHTML,如果不得不使用的話,前端或服務(wù)端必須對輸入進(jìn)行相關(guān)驗(yàn)證,例如對特殊輸入進(jìn)行過濾、轉(zhuǎn)義等處理。前端這邊處理的話,推薦使用白名單過濾?(https://jsxss.com/zh/index.html),通過白名單控制允許的 HTML 標(biāo)簽及各標(biāo)簽的屬性。

        通過用戶提供的對象來創(chuàng)建 React 組件

        舉個(gè)例子:

        //?用戶的輸入
        const?userProvidePropsString?=?`{"dangerouslySetInnerHTML":{"__html":""}}"`;
        //?經(jīng)過?JSON?轉(zhuǎn)換
        const?userProvideProps?=?JSON.parse(userProvidePropsString);
        //?userProvideProps?=?{
        //???dangerouslySetInnerHTML:?{
        //?????"__html":?``
        //??????}
        //?};
        render()?{
        ?????//?出于某種原因解析用戶提供的?JSON?并將對象作為?props?傳遞
        ????return?<div?{...userProvideProps}?/>?
        }

        這段代碼將用戶提供的數(shù)據(jù)進(jìn)行 JSON 轉(zhuǎn)換后直接當(dāng)做 div 的屬性,當(dāng)用戶構(gòu)造了類似例子中的特殊字符串時(shí),頁面就會(huì)被注入惡意代碼,所以要注意平時(shí)在開發(fā)中不要直接使用用戶的輸入作為屬性。

        使用用戶輸入的值來渲染 a 標(biāo)簽的 href 屬性,或類似 img 標(biāo)簽的 src 屬性等

        const?userWebsite?=?"javascript:alert('xss');";
        <a?href={userWebsite}>a>

        如果沒有對該 URL 進(jìn)行過濾以防止通過 javascript:data: 來執(zhí)行 JavaScript,則攻擊者可以構(gòu)造 XSS 攻擊,此處會(huì)有潛在的安全問題。用戶提供的 URL 需要在前端或者服務(wù)端在入庫之前進(jìn)行驗(yàn)證并過濾。

        服務(wù)端如何防止 XSS 攻擊

        服務(wù)端作為最后一道防線,也需要做一些措施以防止 XSS 攻擊,一般涉及以下幾方面:

        • 在接收到用戶輸入時(shí),需要對輸入進(jìn)行盡可能嚴(yán)格的過濾,過濾或移除特殊的 HTML 標(biāo)簽、JS 事件的關(guān)鍵字等。
        • 在輸出時(shí)對數(shù)據(jù)進(jìn)行轉(zhuǎn)義,根據(jù)輸出語境 (html/javascript/css/url),進(jìn)行對應(yīng)的轉(zhuǎn)義
        • 對關(guān)鍵 Cookie 設(shè)置 http-only 屬性,JS腳本就不能訪問到 http-only 的 Cookie 了
        • 利用 CSP (https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CSP) 來抵御或者削弱 XSS 攻擊,一個(gè) CSP 兼容的瀏覽器將會(huì)僅執(zhí)行從白名單域獲取到的腳本文件,忽略所有的其他腳本 (包括內(nèi)聯(lián)腳本和 HTML 的事件處理屬性)

        總結(jié)

        出現(xiàn) XSS 漏洞本質(zhì)上是輸入輸出驗(yàn)證不充分,React 在設(shè)計(jì)上已經(jīng)很安全了,但是一些反模式的寫法還是會(huì)引起安全漏洞。Vue 也是類似,Vue 做的安全措施主要也是轉(zhuǎn)義,HTML 的內(nèi)容和動(dòng)態(tài)綁定的屬性都會(huì)進(jìn)行轉(zhuǎn)義。無論使用 React 或 Vue 等前端框架,都不能百分百的防止 XSS 攻擊,所以服務(wù)端必須對前端參數(shù)做一些驗(yàn)證,包括但不限于特殊字符轉(zhuǎn)義、標(biāo)簽、屬性白名單過濾等。一旦出現(xiàn)安全問題一般都是挺嚴(yán)重的,不管是敏感數(shù)據(jù)被竊取或者用戶資金被盜,損失往往無法挽回。我們平時(shí)開發(fā)中需要保持安全意識,保持代碼的可靠性和安全性。

        小游戲

        看完文章可以嘗試下 XSS 的?小游戲?(https://xss-game.appspot.com/),自己動(dòng)手實(shí)踐模擬 XSS 攻擊,可以對 XSS 有更進(jìn)一步的認(rèn)識。

        1. JavaScript 重溫系列(22篇全)
        2. ECMAScript 重溫系列(10篇全)
        3. JavaScript設(shè)計(jì)模式 重溫系列(9篇全)
        4.?正則 / 框架 / 算法等 重溫系列(16篇全)
        5.?Webpack4 入門(上)||?Webpack4 入門(下)
        6.?MobX 入門(上)?||??MobX 入門(下)
        7. 80+篇原創(chuàng)系列匯總

        回復(fù)“加群”與大佬們一起交流學(xué)習(xí)~

        點(diǎn)擊“閱讀原文”查看 100+ 篇原創(chuàng)文章

        瀏覽 44
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評論
        圖片
        表情
        推薦
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            欧美成人精品三级 | 黄片美女操逼 | 亚洲毛片网 | 精精国产xxxx视频在线观看 | 水蜜桃成人视频 | 亚洲性爱在线 | 国产精品9 | 中国性xxxxx摘花过程 | 国产aⅴ片 | 青青草黄视频 |