Injection SQL et XSS: ce que les hackers du chapeau blanc savent sur la confiance des utilisateurs

Les développeurs de logiciels ont beaucoup à l'esprit. Il y a une myriade de questions à se poser lorsqu'il s'agit de créer un site Web ou une application: quelles technologies utiliserons-nous? Comment l'architecture sera-t-elle mise en place? De quelles fonctions avons-nous besoin? À quoi ressemblera l'interface utilisateur?

Surtout dans un marché du logiciel où l'expédition de nouvelles applications ressemble plus à une course à la réputation qu'à un processus mûrement réfléchi, l'une des questions les plus importantes tombe souvent au bas de la colonne «Urgent»: comment notre produit sera-t-il sécurisé?

Si vous utilisez un cadre robuste et open-source pour créer votre produit (et si un est applicable et disponible, pourquoi ne le feriez-vous pas?), Alors certains problèmes de sécurité de base, comme les jetons CSRF et le cryptage des mots de passe, peuvent déjà être traités pour tu.

Pourtant, les développeurs rapides seraient bien servis pour parfaire leurs connaissances sur les menaces et les pièges courants, ne serait-ce que pour éviter certaines erreurs embarrassantes de recrue. Habituellement, le point le plus faible de la sécurité de votre logiciel, c'est vous.

Je me suis récemment davantage intéressé à la sécurité de l'information en général et à la pratique du piratage éthique en particulier. Un hacker éthique, parfois appelé hacker «chapeau blanc», et parfois simplement «hacker», est quelqu'un qui recherche d'éventuelles vulnérabilités de sécurité et les signale de manière responsable (en privé) aux propriétaires de projet.

En revanche, un hacker malveillant ou «black hat», également appelé «cracker», est quelqu'un qui exploite ces vulnérabilités à des fins de divertissement ou de gain personnel.

Les pirates au chapeau blanc et au chapeau noir peuvent utiliser les mêmes outils et ressources et essaient généralement de se rendre dans des endroits qu'ils ne sont pas censés être. Mais les chapeaux blancs le font avec la permission et avec l'intention de fortifier les défenses au lieu de les détruire. Les chapeaux noirs sont les méchants.

Quand il s'agit d'apprendre à trouver des vulnérabilités de sécurité, il n'est pas surprenant que je dévore toutes les informations sur lesquelles je peux mettre la main. Cet article est un condensé de certains domaines clés qui sont particulièrement utiles aux développeurs lors de la gestion des entrées utilisateur. Ces leçons ont été collectées à partir de ces excellentes ressources:

  • Les guides du projet Open Web Application Security
  • La playlist Hacker101 de la chaîne YouTube de HackerOne
  • Web Hacking 101 par Peter Yaworski
  • Le blog de Brute Logic
  • La chaîne YouTube Computerphile
  • Vidéos mettant en vedette Jason Haddix (@jhaddix) et Tom Hudson (@tomnomnom) (deux hackers éthiques accomplis avec des méthodologies différentes mais efficaces)

Vous connaissez peut-être le slogan «nettoyez vos entrées!» Cependant, comme j'espère que cet article le démontre, développer une application avec une sécurité robuste n'est pas aussi simple.

Je suggère une autre phrase: faites attention à vos contributions. Développons en examinant les attaques les plus courantes qui tirent parti des vulnérabilités dans ce domaine: l'injection SQL et les scripts intersites.

Attaques par injection SQL

Si vous n'êtes pas encore familiarisé avec les attaques par injection SQL (Structured Query Language) ou SQLi, voici une excellente vidéo expliquant comme je suis cinq sur SQLi. Vous connaissez peut-être déjà cette attaque des Little Bobby Tables de xkcd.

Essentiellement, les acteurs malveillants peuvent envoyer des commandes SQL qui affectent votre application via certaines entrées de votre site, comme un champ de recherche qui extrait les résultats de votre base de données. Les sites codés en PHP peuvent être particulièrement sensibles à ceux-ci, et une attaque SQL réussie peut être dévastatrice pour les logiciels qui reposent sur une base de données (comme dans, votre table Users est maintenant un pot de pétunias).

Un moniteur avec une commande SQL Select qui récupère toute votre base

Vous pouvez tester votre propre site pour voir si vous êtes vulnérable à ce type d'attaque. (Veuillez ne tester que les sites que vous possédez, car exécuter des injections SQL où vous n'avez pas l'autorisation de le faire est peut-être illégal dans votre localité; et certainement, universellement, pas très drôle.) Les charges utiles suivantes peuvent être utilisées pour entrées de test:

  • ' OR 1='1 prend la valeur d'une constante true et, en cas de succès, renvoie toutes les lignes de la table.
  • ' AND 0='1 évalue à une constante false, et en cas de succès, ne renvoie aucune ligne.

Cette vidéo présente les tests ci-dessus et montre à quel point une attaque par injection SQL peut avoir un impact.

Heureusement, il existe des moyens d'atténuer les attaques par injection SQL, et ils se résument tous à un concept de base: ne faites pas confiance aux entrées de l'utilisateur.

Atténuation de l'injection SQL

Afin d'atténuer efficacement les injections SQL, les développeurs doivent empêcher les utilisateurs de soumettre avec succès des commandes SQL brutes à n'importe quelle partie du site.

Certains cadres feront le gros du travail pour vous. Par exemple, Django implémente le concept de mappage objet-relationnel, ou ORM, avec son utilisation de QuerySets. Nous pouvons les considérer comme des fonctions wrapper qui aident votre application à interroger la base de données à l'aide de méthodes prédéfinies qui évitent l'utilisation de SQL brut.

Cependant, pouvoir utiliser un framework n'est jamais une garantie. Lorsque vous traitez directement avec une base de données, il existe d'autres méthodes que nous pouvons utiliser pour extraire en toute sécurité nos requêtes SQL de l'entrée utilisateur, même si leur efficacité varie. Ceux-ci sont, par ordre du plus au moins préféré, et avec des liens vers des exemples pertinents:

  1. Instructions préparées avec liaison de variable (ou requêtes paramétrées),
  2. Procédures stockées; et
  3. Liste blanche ou entrée utilisateur qui s'échappe.

Si vous souhaitez mettre en œuvre les techniques ci-dessus, les cheatsheets liés sont un excellent point de départ pour creuser plus profondément. Il suffit de dire que l'utilisation de ces techniques pour obtenir des données au lieu d'utiliser des requêtes SQL brutes permet de minimiser les chances que SQL soit traité par n'importe quelle partie de votre application qui prend des entrées des utilisateurs, atténuant ainsi les attaques par injection SQL.

La bataille, cependant, n'est qu'à moitié gagnée ...

Attaques de type Cross Site Scripting (XSS)

Si vous êtes un codeur malveillant, JavaScript est à peu près votre meilleur ami. Les bonnes commandes feront tout ce qu'un utilisateur légitime peut faire (et même certaines choses qu'il n'est pas censé pouvoir faire) sur une page Web, parfois sans aucune interaction de la part d'un utilisateur réel.

Les attaques de type Cross Site Scripting, ou XSS, se produisent lorsque du code JavaScript est injecté dans une page Web et modifie le comportement de cette page. Ses effets peuvent aller d'occurrences nuisibles de farce à des contournements d'authentification plus sévères ou au vol d'informations d'identification. Ce rapport d'incident d'Apache en 2010 est un bon exemple de la façon dont XSS peut être enchaîné dans une attaque plus importante pour prendre le contrôle de comptes et de machines.

Une soirée dansante HTML avec un petit coup de JS

XSS can occur on the server or on the client side, and generally comes in three flavors: DOM (Document Object Model) based, stored, and reflected XSS. The differences amount to where the attack payload is injected into the application.

DOM based XSS

DOM based XSS occurs when a JavaScript payload affects the structure, behavior, or content of the web page the user has loaded in their browser. These are most commonly executed through modified URLs, such as in phishing.

To see how easy it would be for injected JavaScript to manipulate a page, we can create a working example with an HTML web page. Try creating a file on your local system called xss-test.html (or whatever you like) with the following HTML and JavaScript code:

  My XSS Example   

Hello there!

var name = new URLSearchParams(document.location.search).get('name'); if (name !== 'null') { document.getElementById('greeting').innerHTML = 'Hello ' + name + '!'; }

This web page will display the title “Hello there!” unless it receives a URL parameter from a query string with a value for name. To see the script work, open the page in a browser with an appended URL parameter, like so:

file:///path/to/file/xss-test.html?name=Victoria

Fun, right? Our insecure (in the safety sense, not the emotional one) page takes the URL parameter value for name and displays it in the DOM. The page is expecting the value to be a nice friendly string, but what if we change it to something else? Since the page is owned by us and only exists on our local system, we can test it all we like. What happens if we change the name parameter to, say, ?

Une capture d'écran de l'exemple de page XSS

This is just one example, largely based on one from Brute’s post, that demonstrates how an XSS attack could be executed. Funny pop-up alerts may be amusing, but JavaScript can do a lot of harm, including helping malicious attackers steal passwords and personal information.

Stored and reflected XSS

Stored XSS occurs when the attack payload is stored on the server, such as in a database. The attack affects a victim whenever that stored data is retrieved and rendered in the browser. For example, instead of using a URL query string, an attacker might update their profile page on a social site to include a hidden script in, say, their “About Me” section. The script, improperly stored on the site’s server, would successfully execute at a later time when another user views the attacker’s profile.

One of the most famous examples of this is the Samy worm that all but took over MySpace in 2005. It propogated by sending HTTP requests that replicated it onto a victim’s profile page whenever an infected profile was viewed. Within just 20 hours, it had spread to over a million users.

Reflected XSS similarly occurs when the injected payload travels to the server, however, the malicious code does not end up stored in a database. It is instead immediately returned to the browser by the web application.

An attack like this might be executed by luring the victim to click a malicious link that sends a request to the vulnerable website’s server. The server would then send a response to the attacker as well as the victim, which may result in the attacker being able to obtain passwords, or perpetrate actions that appear to originate from the victim.

XSS attack mitigation

In all of these cases, XSS attacks can be mitigated with two key strategies: validating form fields, and avoiding the direct injection of user input on the web page.

Validating form fields

Frameworks can again help us out when it comes to making sure that user-submitted forms are on the up-and-up. One example is Django’s built-in Field classes, which provide fields that validate to some commonly used types and also specify sane defaults. Django’s EmailField, for instance, uses a set of rules to determine if the input provided is a valid email. If the submitted string has characters in it that are not typically present in email addresses, or if it doesn’t imitate the common format of an email address, then Django won’t consider the field valid and the form will not be submitted.

If relying on a framework isn’t an option, we can implement our own input validation. This can be accomplished with a few different techniques, including type conversion, for example, ensuring that a number is of type int(); checking minimum and maximum range values for numbers and lengths for strings; using a pre-defined array of choices that avoids arbitrary input, for example, months of the year; and checking data against strict regular expressions.

Thankfully, we needn’t start from scratch. Open source resources are available to help, such as the OWASP Validation Regex Repository, which provides patterns to match against for some common forms of data. Many programming languages offer validation libraries specific to their syntax, and we can find plenty of these on GitHub. Additionally, the XSS Filter Evasion Cheat Sheet has a couple suggestions for test payloads we can use to test our existing applications.

While it may seem tedious, properly implemented input validation can protect our application from being susceptible to XSS.

Avoiding direct injection

Elements of an application that directly return user input to the browser may not, on a casual inspection, be obvious. We can determine areas of our application that may be at risk by exploring a few questions:

  • How does data flow through our application?
  • What does a user expect to happen when they interact with this input?
  • Where on our page does data appear? Does it become embedded in a string or an attribute?

Here are some sample payloads that we can play with in order to test inputs on our site (again, only our own site!) courtesy of Hacker101. The successful execution of any of these samples can indicate a possible XSS vulnerability due to direct injection.

  • ">

    test

  • '+alert(1)+'
  • "onmouserover="alert(1)
  • //"onmouseover="alert(1)

As a general rule, if you are able to design around directly injecting input, do so. Alternatively, be sure to completely understand the effect of the methods you choose; for example, using innerText instead of innerHTML in JavaScript will ensure that content will be set as plain text instead of (potentially vulnerable) HTML.

Pay attention to your inputs

Les développeurs de logiciels sont nettement désavantagés lorsqu'il s'agit de rivaliser avec des pirates informatiques ou des pirates malveillants. Pour tout le travail que nous faisons pour sécuriser chaque entrée qui pourrait potentiellement compromettre notre application, un attaquant n'a qu'à trouver celle que nous avons manquée. C'est comme installer des pênes dormants sur toutes les portes, mais en laissant une fenêtre ouverte!

Cependant, en apprenant à penser comme un attaquant, nous pouvons mieux préparer notre logiciel à se dresser contre les mauvais acteurs. Aussi passionnant que cela puisse être de livrer des fonctionnalités le plus rapidement possible, nous éviterons d'accumuler beaucoup de dettes de sécurité si nous prenons le temps à l'avance de réfléchir au flux de notre application, de suivre les données et de prêter attention à nos entrées.