L'auteur de l'article, dont nous publions la traduction aujourd'hui, dit que React est sa bibliothèque préférée pour créer des interfaces interactives. React est à la fois facile à utiliser et assez bien protégé. Cependant, cela ne signifie pas que les applications React sont complètement invulnérables. Il est très facile de tomber dans un calme injustifié, ayant décidé que vous ne pouvez pas vous inquiéter des attaques XSS car le projet utilise React.
Les vulnérabilités dans React surviennent le plus souvent lorsque le développeur pense qu'il utilise les mécanismes de protection de cette bibliothèque, bien qu'en fait il s'avère que ce n'est pas le cas. Par conséquent, il est important d'évaluer correctement les capacités de React et de savoir quelles tâches un programmeur doit résoudre seul.

Aujourd'hui, nous allons parler des vulnérabilités typiques de React, comment les trouver lors d'une révision de code et comment se défendre contre elles.
Premier (très court) exemple de script intersite
Le cross site scripting (XSS) est une vulnérabilité client qui peut entraîner de graves problèmes. Les attaques XSS se produisent lorsqu'un attaquant est en mesure de tromper un site Web et de le forcer à exécuter du code JavaScript arbitraire dans les navigateurs de ses utilisateurs.
L'attaque XSS réfléchie est effectuée au moyen d'un lien contenant des informations textuelles qui sont traitées par le navigateur sous forme de code. Par exemple, il s'agit d'un champ de formulaire dans lequel, côté client, un texte de demande spéciale est saisi.
Une attaque XSS stockée est une situation dans laquelle un attaquant a accès au serveur et lorsque le code exécuté sur le serveur génère ce qui arrive à la page Web du client. Les vecteurs typiques de telles attaques sont le téléchargement de commentaires et d'images sur les serveurs.
Le ver
Samy a exploité la vulnérabilité MySSpace XSS. C'était l'un des virus qui se propageait le plus rapidement de tous les temps.
Les sites Web vulnérables peuvent exposer leurs utilisateurs au vol de mots de passe ou de données personnelles. Et c'est la manière habituelle d'exploiter d'autres vulnérabilités. Les scripts malveillants sont le plus souvent utilisés pour envoyer du spam et rediriger les utilisateurs vers des sites frauduleux. Cela peut nuire à la réputation et aux performances SEO d'un site attaqué avec succès.
Vulnérabilité n ° 1: contrôle de l'état initial de la page, utilisé lors du rendu du serveur
Parfois, lorsque nous formons l'état initial d'une page, nous, ce qui est dangereux, créons un document basé sur une chaîne JSON. Cette vulnérabilité dans le code ressemble à ceci:
<script>window.__STATE__ = ${JSON.stringify({ data })}</script>
Cela est dangereux car la méthode
JSON.stringify()
, sans «penser» à quoi que ce soit, convertit toutes les données qui lui sont fournies sous forme de chaîne (tant qu'il s'agit de données JSON valides), ce qui est ce que sera affiché sur la page. Si
{ data }
contient des champs qu'un utilisateur non fiable peut modifier, comme un nom d'utilisateur ou des informations utilisateur, quelque chose comme le suivant peut être incorporé dans ces champs:
{ username: "pwned", bio: "</script><script>alert('XSS Vulnerability!')</script>" }
Ce modèle est souvent utilisé dans le rendu côté serveur des applications React qui utilisent Redux. Il était présent dans la documentation officielle de Redux, et en conséquence, de nombreux didacticiels et exemples de modèles d'application qui peuvent être trouvés sur GitHub l'utilisent toujours.
Ne croyez pas? Alors voyez par vous-même. Cherchez dans Google le texte "réagir à l'exemple d'application de rendu côté serveur" et essayez cette attaque sur l'un des résultats de recherche de la première page.
▍ Identification de la vulnérabilité lors de la révision du code
Recherchez les appels à la méthode
JSON.stringify()
qui acceptent les variables pouvant contenir des données non fiables dans la balise de
script
. Voici un exemple qui figurait dans la documentation Redux:
function renderFullPage(html, preloadedState) { return ` <!doctype html> <html> <head> <title>Redux Universal Example</title> </head> <body> <div id="root">${html}</div> <script> window.__PRELOADED_STATE__ = ${JSON.stringify(preloadedState)} </script> <script src="/static/bundle.js"></script> </body> </html> ` }
Et voici un morceau de code de l'exemple d'application trouvé sur GitHub:
function htmlTemplate( reactDom, reduxState, helmetData ) { return ` <!DOCTYPE html> <html> <head> <meta charset="utf-8"> ${ helmetData.title.toString( ) } ${ helmetData.meta.toString( ) } <title>React SSR</title> <link rel="stylesheet" type="text/css" href="./styles.css" /> </head> <body> <div id="app">${ reactDom }</div> <script> window.REDUX_DATA = ${ JSON.stringify( reduxState ) } </script> <script src="./app.bundle.js"></script> </body> </html> `; }
Parfois, trouver cette vulnérabilité est un peu plus difficile. Le code suivant s'avérera également dangereux - si l'échappement correct de
context.data
pas effectué:
const RenderedApp = htmlData.replace('{{SSR}}', markup) .replace('<meta-head/>', headMarkup) .replace('{{data}}', new ArrayBuffer(JSON.stringify(context.data)).toString('base64'))
Lors du rendu côté serveur, faites attention à ce qui est rendu exactement. Si ce que l'utilisateur entre n'est pas correctement protégé et affiché dans le DOM, cela peut être dangereux.
▍ Protection
L'une des options de protection contre cette vulnérabilité consiste à utiliser le module npm
serialize-javascript
, conçu pour protéger le JSON de sortie. Si vous effectuez un rendu de serveur dans un environnement non-Node.js, vous devrez choisir le package approprié pour votre langue.
Voici la commande pour installer le module:
$ npm install --save serialize-javascript
Après cela, vous devez l'importer dans un fichier et réécrire le code précédemment vulnérable traitant de la
window
suit:
<script>window.__STATE__ = ${ serialize( data, { isJSON: true } ) }</script>
Voici un excellent article sur ce sujet.
Vulnérabilité №2: liens insidieux
La balise
<a>
peut avoir l'attribut
href
, qui contient un lien vers une autre page du site, vers un autre site, vers un endroit de la page en cours. Les liens peuvent contenir des scripts qui ressemblent à ceci:
javascript: stuff()
. Si vous ne connaissiez pas cette fonctionnalité HTML, essayez-la dès maintenant en copiant le code suivant dans la ligne du navigateur:
data:text/html, <a href="javascript: alert('hello from javascript!')" >click me</a>
Pour les développeurs Web, cela signifie que si le contenu des liens est défini en fonction des données saisies par l'utilisateur, l'attaquant peut ajouter du code malveillant commençant par
javascript:
à ces données. Ensuite, si l'utilisateur clique sur un mauvais lien, un script attaquant sera lancé dans le navigateur.
Cette vulnérabilité n'est certainement pas seulement caractéristique de React, mais c'est l'un des problèmes que les développeurs React rencontrent souvent lorsqu'ils s'attendent à ce que la valeur correspondante soit automatiquement correctement échappée. Il convient de noter que dans une
future version de React, ce problème sera moins aigu.
▍ Identification de la vulnérabilité lors de la révision du code
Les utilisateurs du projet peuvent-ils ajouter des liens vers des pages sur lesquelles d'autres utilisateurs peuvent cliquer? Si c'est le cas, essayez d'ajouter un «lien» à la page comme suit:
javascript: alert("You are vulnerable to XSS!")
Si la boîte de message correspondante s'affiche en cliquant sur le lien, cela signifie que le projet est vulnérable aux attaques XSS. Essayez-le partout où vous pouvez ajouter des liens. Il est probable que tous ces endroits ne seront pas vulnérables.
▍ Protection
La protection contre cette vulnérabilité ne convient pas uniquement aux projets React. Ce qui doit être fait exactement dépend de l'application. En outre, vous devrez peut-être effectuer des corrections sur le serveur.
Vous pourriez penser que pour résoudre le problème, il suffit de supprimer le préfixe
javascript:
des données. Ceci est un exemple d'utilisation de la stratégie de liste noire, qui ne peut pas être considérée comme réussie dans le
nettoyage des données . Les pirates ont des moyens ingénieux pour contourner ces filtres, donc au lieu d'un tel mouvement (ou en plus), assurez-vous que les liens utilisent un protocole sur liste blanche (par exemple,
http:
et échappent aux entités HTML.
Voici un article détaillé sur ce sujet concernant le blindage des propriétés qu'un attaquant peut contrôler.
Une autre stratégie qui peut ajouter un niveau de protection supplémentaire au projet consiste à utiliser le mécanisme d'ouverture de liens personnalisés dans de nouveaux onglets de navigateur. Cependant, je ne recommanderais pas d'utiliser cette stratégie comme la seule "ligne de défense" du projet. L'ouverture de liens
javascript:
dans un nouvel onglet est un exemple du comportement non standard des éléments de page. La plupart des navigateurs exécuteront le script dans un onglet vide sans nuire à l'utilisateur, mais ce n'est pas garanti, et vous pouvez probablement contourner ce problème, qui dépend du navigateur.
Envisagez d'utiliser le composant spécial
UserLink , ce qui entraînera le fait que la balise
<a>
vulnérable aura moins de chances d'accéder aux pages de votre projet à l'avenir. En outre, il convient d'ajouter quelques tests et règles de peluchage au projet, visant à identifier le code potentiellement dangereux et à l'empêcher de se mettre en production.
Les liens ne sont pas les seules entités pouvant être utilisées de cette manière. Mais ils sont la cible d'attaque la plus probable dans les applications React. Tout élément peut être vulnérable à cette attaque si l'attaquant peut contrôler sa valeur
URI
. Une autre possibilité de réaliser cette attaque, par exemple, est une conception de vue. Une liste complète des attributs pouvant contenir des URI peut être trouvée dans
cette liste en utilisant le mot-clé
%URI
utilisant la recherche du navigateur (
Ctrl+F
).
Vulnérabilité n ° 3: mal comprendre la signification de la construction dangereusement SetInnerHtml
Je suis extrêmement reconnaissant à React que l'avertissement de sécurité se trouve directement dans le nom de la méthode. C'est le nom
dangerouslySetInnerHTML
. Malgré cet avertissement, nous sommes encore souvent confrontés au fait que les développeurs prennent des risques en effectuant des opérations dangereuses. On peut en dire autant de
eval()
.
Prenons l'exemple suivant que j'ai trouvé sur le site à partir de la première page des résultats de recherche Google:
<script dangerouslySetInnerHTML={{ __html: `window.__PRELOADED_STATE__ = ${JSON.stringify(initialState)};`}}></script>
Ceci est un exemple de vulnérabilité n ° 1, mais avec une fonctionnalité qui devrait immédiatement attirer l'attention sur le fait que quelque chose ne va pas ici. Là où j'ai trouvé cela, une tentative a été faite pour expliquer: "Nous utilisons
dangerouslySetInnerHTML comme méthode de nettoyage des données et de prévention des attaques XSS." Et bien non! C'est faux. Ne le faites pas. Pour plus d'informations sur
dangerouslySetInnerHTML
, lisez la documentation React.
Un autre exemple que cela se produit en permanence est la façon dont les membres de la même équipe ont découvert qu'ils avaient une
vulnérabilité lorsqu'ils ont ajouté un balisage Markdown à la page en utilisant
dangerouslySetInnerHTML
. Afin de s'en protéger à l'avenir, ils ont commencé à utiliser une règle spéciale de peluchage.
▍ Identification de la vulnérabilité lors de la révision du code
Avant d'envoyer des demandes de tirage ou d'effectuer des opérations de fusion, il est utile de rechercher dans le code
dangerouslySetInnerHTML
chaînes
dangerouslySetInnerHTML
et
eval
(je recherche également les commandes
console.log
cette façon) ou d'utiliser la règle de linter correspondante.
▍ Protection
Assurez-vous que dans tous les cas d'utilisation de la méthode
dangerouslySetInnerHTML
, seules les données fiables peuvent être chargées sur la page. Comment savoir si les données sont fiables? Si quelque chose ne vient pas de vous, cela peut être une menace. Cela inclut les données téléchargées à partir des API externes et ce qui est émis à l'aide des outils Markdown.
Note d'usurpation de composant
En 2015,
quelqu'un a découvert que vous pouvez usurper des composants en transmettant JSON aux composants qui attendent du texte. J'ai pu trouver un seul cas de message d'usurpation de composant et une
longue discussion causée par ce message. La discussion a porté sur les responsabilités de React dans la prévention des attaques XSS. En conséquence, les développeurs de React ont publié un
correctif qui semble avoir aidé à corriger cette vulnérabilité.
J'ai décidé de ne pas inclure une histoire sur cette vulnérabilité dans l'article, mais ce sujet peut être intéressant pour d'autres recherches.
Remarque SSR
La vulnérabilité de rendu du serveur est tellement répandue du fait qu'elle était présente dans la documentation Redux et, par conséquent, répartie sur de nombreux autres matériaux. Ce problème a été résolu en 2016. Mais même aujourd'hui, après trois ans, des guides débutants disséminés sur Internet enseignent toujours ce que vous ne devriez pas enseigner.
Au fait, voici vos devoirs: trouvez un exemple de ce problème sur GitHub et envoyez une demande d'extraction pour le résoudre.
Voici un exemple .
Ensemble, nous pouvons une fois pour toutes nous débarrasser de cette vulnérabilité!
Chers lecteurs! Avez-vous rencontré des attaques sur vos projets React?
