SQL Injection und XSS: Was White Hat-Hacker über das Vertrauen in Benutzereingaben wissen

Softwareentwickler haben viel im Kopf. Beim Erstellen einer Website oder Anwendung gibt es unzählige Fragen: Welche Technologien werden wir verwenden? Wie wird die Architektur aufgebaut? Welche Funktionen brauchen wir? Wie wird die Benutzeroberfläche aussehen?

Insbesondere in einem Softwaremarkt, in dem der Versand neuer Apps eher ein Wettlauf um den Ruf als ein wohlüberlegter Prozess zu sein scheint, steht eine der wichtigsten Fragen häufig ganz unten in der Spalte „Dringend“: Wie wird unser Produkt gesichert?

Wenn Sie ein robustes Open-Source-Framework für die Erstellung Ihres Produkts verwenden (und wenn eines anwendbar und verfügbar ist, warum nicht?), Werden möglicherweise bereits einige grundlegende Sicherheitsbedenken wie CSRF-Token und Kennwortverschlüsselung behandelt Sie.

Trotzdem wären schnelllebige Entwickler gut beraten, ihr Wissen über häufige Bedrohungen und Fallstricke aufzufrischen, wenn auch nur, um einige peinliche Anfängerfehler zu vermeiden. Normalerweise sind Sie der schwächste Punkt in der Sicherheit Ihrer Software .

Ich habe mich in letzter Zeit mehr für Informationssicherheit im Allgemeinen und für ethisches Hacken im Besonderen interessiert. Ein ethischer Hacker, manchmal als "White Hat" -Hacker und manchmal nur als "Hacker" bezeichnet, ist jemand, der nach möglichen Sicherheitslücken sucht und diese verantwortungsbewusst (privat) den Projektbesitzern meldet.

Im Gegensatz dazu ist ein böswilliger oder "Black Hat" -Hacker, auch "Cracker" genannt, jemand, der diese Sicherheitslücken zur Unterhaltung oder zum persönlichen Vorteil ausnutzt.

Sowohl White-Hat- als auch Black-Hat-Hacker verwenden möglicherweise dieselben Tools und Ressourcen und versuchen im Allgemeinen, an Orte zu gelangen, an denen sie eigentlich nicht sein sollten. Aber weiße Hüte tun dies mit Erlaubnis und mit der Absicht, die Verteidigung zu stärken, anstatt sie zu zerstören. Schwarze Hüte sind die Bösen.

Wenn es darum geht, Sicherheitslücken zu finden, sollte es keine Überraschung sein, dass ich alle Informationen verschlungen habe, die ich in die Hände bekommen kann. Dieser Beitrag ist eine Zusammenfassung einiger Schlüsselbereiche, die Entwicklern bei der Verarbeitung von Benutzereingaben besonders hilfreich sind. Diese Lektionen wurden gemeinsam aus diesen hervorragenden Ressourcen gewonnen:

  • Die Open Web Application Security-Projekthandbücher
  • Die Hacker101-Wiedergabeliste aus dem YouTube-Kanal von HackerOne
  • Web Hacking 101 von Peter Yaworski
  • Der Blog von Brute Logic
  • Der YouTube-Kanal von Computerphile
  • Videos mit Jason Haddix (@jhaddix) und Tom Hudson (@tomnomnom) (zwei versierte ethische Hacker mit unterschiedlichen, aber beide effektiven Methoden)

Möglicherweise kennen Sie das Schlagwort „Bereinigen Sie Ihre Eingaben!“. Wie dieser Beitrag jedoch hoffentlich zeigt, ist die Entwicklung einer Anwendung mit robuster Sicherheit nicht ganz so einfach.

Ich schlage einen alternativen Satz vor: Achten Sie auf Ihre Eingaben. Lassen Sie uns die häufigsten Angriffe untersuchen, die Schwachstellen in diesem Bereich ausnutzen: SQL-Injection und Cross-Site-Scripting.

SQL-Injection-Angriffe

Wenn Sie noch nicht mit SQL-Injection-Attacken (Structured Query Language) oder SQLi vertraut sind, finden Sie hier ein großartiges Video, in dem Sie erklären, wie ich fünf bin. Sie kennen diesen Angriff möglicherweise bereits aus den Little Bobby Tables von xkcd.

Im Wesentlichen können böswillige Akteure möglicherweise SQL-Befehle senden, die sich auf Ihre Anwendung auswirken, und zwar über Eingaben auf Ihrer Website, z. B. ein Suchfeld, mit dem Ergebnisse aus Ihrer Datenbank abgerufen werden. In PHP codierte Sites können besonders anfällig für diese sein, und ein erfolgreicher SQL-Angriff kann für Software, die auf einer Datenbank basiert, verheerend sein (wie in, ist Ihre Benutzertabelle jetzt ein Topf voller Petunien).

Ein Monitor mit einem SQL Select-Befehl, der Ihre gesamte Basis abruft

Sie können Ihre eigene Website testen, um festzustellen, ob Sie für diese Art von Angriff anfällig sind. (Bitte testen Sie nur Websites, die Sie besitzen, da das Ausführen von SQL-Injections, für die Sie keine Berechtigung dazu haben, an Ihrem Standort möglicherweise illegal und auf jeden Fall allgemein nicht sehr lustig ist.) Die folgenden Nutzdaten können verwendet werden Testeingaben:

  • ' OR 1='1 wird zu einer Konstanten true ausgewertet und gibt bei Erfolg alle Zeilen in der Tabelle zurück.
  • ' AND 0='1 wird zu einer Konstanten false ausgewertet und gibt bei Erfolg keine Zeilen zurück.

Dieses Video zeigt die oben genannten Tests und zeigt hervorragend, wie effektiv ein SQL-Injection-Angriff sein kann.

Zum Glück gibt es Möglichkeiten, SQL-Injection-Angriffe abzuschwächen, und alle beschränken sich auf ein Grundkonzept: Vertrauen Sie den Benutzereingaben nicht.

SQL Injection Mitigation

Um SQL-Injektionen effektiv zu reduzieren, müssen Entwickler verhindern, dass Benutzer erfolgreich SQL-Rohbefehle an einen beliebigen Teil der Site senden können.

Einige Frameworks erledigen den größten Teil des schweren Hebens für Sie. Beispielsweise implementiert Django das Konzept der objektrelationalen Zuordnung (ORM) unter Verwendung von QuerySets. Wir können uns diese als Wrapper-Funktionen vorstellen, mit denen Ihre Anwendung die Datenbank mit vordefinierten Methoden abfragen kann, die die Verwendung von Raw-SQL vermeiden.

Die Verwendung eines Frameworks ist jedoch niemals eine Garantie. Beim direkten Umgang mit einer Datenbank gibt es andere Methoden, mit denen wir unsere SQL-Abfragen sicher von Benutzereingaben abstrahieren können, obwohl sie sich in ihrer Wirksamkeit unterscheiden. Diese sind in der Reihenfolge der am meisten bis am wenigsten bevorzugten und mit Links zu relevanten Beispielen:

  1. Vorbereitete Anweisungen mit variabler Bindung (oder parametrisierten Abfragen),
  2. Gespeicherte Prozeduren; und
  3. Whitelisting oder Escape-Benutzereingaben.

Wenn Sie die oben genannten Techniken implementieren möchten, sind die verknüpften Cheatsheets ein guter Ausgangspunkt, um tiefer zu graben. Es genügt zu sagen, dass die Verwendung dieser Techniken zum Abrufen von Daten anstelle von SQL-Rohabfragen dazu beiträgt, die Wahrscheinlichkeit zu minimieren, dass SQL von einem Teil Ihrer Anwendung verarbeitet wird, der Eingaben von Benutzern entgegennimmt, und so SQL-Injection-Angriffe abschwächt.

Die Schlacht ist jedoch nur zur Hälfte gewonnen ...

XSS-Angriffe (Cross Site Scripting)

Wenn Sie ein böswilliger Programmierer sind, ist JavaScript so ziemlich Ihr bester Freund. Die richtigen Befehle tun alles, was ein legitimer Benutzer auf einer Webseite tun kann (und sogar einige Dinge, die er eigentlich nicht kann), manchmal ohne Interaktion seitens eines tatsächlichen Benutzers.

Cross Site Scripting-Angriffe (XSS) treten auf, wenn JavaScript-Code in eine Webseite eingefügt wird und das Verhalten dieser Seite ändert. Die Auswirkungen können von Streichstörungen bis hin zu schwerwiegenderen Umgehungen der Authentifizierung oder dem Diebstahl von Anmeldeinformationen reichen. Dieser Vorfallbericht von Apache aus dem Jahr 2010 ist ein gutes Beispiel dafür, wie XSS bei einem größeren Angriff verkettet werden kann, um Konten und Computer zu übernehmen.

Eine HTML-Tanzparty mit ein wenig 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, ?

Ein Screenshot des XSS-Seitenbeispiels

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

Softwareentwickler sind im Wettbewerb mit Black Hat- oder böswilligen Hackern deutlich benachteiligt. Bei all der Arbeit, die wir tun, um jede Eingabe zu sichern, die unsere Anwendung möglicherweise gefährden könnte, muss ein Angreifer nur diejenige finden, die wir verpasst haben. Es ist, als würde man an allen Türen Riegel anbringen, aber ein Fenster offen lassen!

Indem wir lernen, wie ein Angreifer zu denken, können wir unsere Software besser darauf vorbereiten, sich gegen schlechte Schauspieler zu behaupten. So aufregend es auch sein mag, Funktionen so schnell wie möglich zu liefern, wir vermeiden es, eine Menge Sicherheitsschulden zu verursachen, wenn wir uns vorher die Zeit nehmen, den Ablauf unserer Anwendung zu überdenken, den Daten zu folgen und auf unsere Eingaben zu achten.