Skip to main content
swissICT Booster  |  M&F Academy  |  M&F Events   |  +41 44 747 44 44  | 
11 Minuten Lesezeit (2276 Worte)

WebApp Security: Grundlagen, die man kennen sollte

Mit der steigenden Digitalisierung und dem Wechsel zu Webtechnologien und Cloud, der durch die Homeoffice Pflicht während der Corona Pandemie nochmals beschleunigt wurde, ist die WebApp Security immer wichtiger geworden. Im Rahmen eines zweitägigen Workshops haben uns Urs Müller und Mischa Bachmann von der Firma Compass Security verschiedene Themen dieses Bereichs nähergebracht.

Die wichtigsten Informationen habe ich euch in diesem Blogbeitrag zusammengefasst.

SQL Injection

Ein sehr bekanntes, aber auch wichtiges Thema ist die SQL Injection. Die Gefahr einer SQL Injection besteht immer dann, wenn eine Benutzereingabe in einer Abfrage an die Datenbank verwendet wird. Dies ist oft der Fall, da die WebApp auf Eingaben des Benutzers reagieren soll. Ein Beispiel ist das Anzeigen eines Benutzerkontos. Dies kann dann so aussehen:

SQL Query

Das Problem hierbei ist, dass neben den Abfragen, die durch die normale Benutzung der Webapp erfolgen, auch spezielle zusammengestellte Abfragen gemacht werden können, die dann einen vom Entwickler unvorhergesehenen Effekt haben.

Beispiel: Wenn wir im obigen Beispiel den Benutzer durch den folgenden Ausdruck ersetzen

SQL Injection

bekommen wir alle Einträge aus der „users“ Tabelle, da der Ausdruck ‘’=’’ immer als wahr ausgewertet wird.

Mit der gleichen Methode ist es auch möglich, die Namen aller Tabellen in der Datenbank herauszufinden, sowie auch Daten aus anderen Tabellen auszulesen. Dies betrifft auch Tabellen, welche die Passwörter aller Benutzer enthält. Es gibt sogar Tools, die automatisch nach SQL Injection Schwachstellen suchen und diese ausnützen können.

Wenn man in der Designphase einer Applikation die Gefahr einer SQL Injection berücksichtigt, dann ist es unter üblichen Umständen weitgehend möglich diese zu verhindern. Die grösste Gefahr besteht dort, wo man unsicheren Input direkt in die SQL Query einbaut. Eine Input Validation, die nicht erlaubte Charakter encodiert oder solche Anfragen verwirft, reicht schon, um die meisten Angriffe verhindern zu können. Noch sicherer ist das Verwenden von Prepared Statements. Durch diese werden die Abfrage selbst und die Daten der Abfrage separiert. Es ist somit nicht mehr möglich, mit den Eingabedaten die vordefinierte Abfrage zu verändern. Prepared Statements sind aber nur sicher, wenn man SQL Literals modifizieren möchte. Sollen Identifier, wie zum Beispiel der Name der Spalte, oder Syntax Keywords, so wie ASC oder DESC, dynamisch einfügt werden, dann schützt ein Prepared Statement auch nicht. In diesem Fall sollte man besser auf eine Whitelist mit erlaubten Identifier zurückgreifen.

Cross Site Scripting

Wie bei der SQL Injection liegt das Problem von XSS (Cross Site Scripting) auch in unsicheren Inhalten, die ein Benutzer eingeben kann, um nicht vorhergesehenes Verhalten zu generieren. Bei XSS wird die Eingabe nicht an die Datenbank weitergegeben, sondern der Input verändert direkt das Verhalten der WebApp. Somit kann man die WebApp modifizieren und eine ungeplante Fehlfunktion generieren. Dies passiert, wenn die Eingaben nicht sicher in das HTML eigebettet werden. Wenn der Benutzer also eine Eingabe macht, die valides HTML darstellt, wird dies automatisch ausgewertet und dargestellt. Mit Hilfe eines Script Tags ist es sogar möglich, beliebigen JS Code auszuführen:

XSS

Die bösartige Benutzereingabe kann wie im Bild via URL Parameter aber auch via einen gespeicherten Wert, zum Beispiel eine Bewertung eines Produkts, injiziert werden. So kann ein Angreifer beliebigen JS Code auf dem Client ausführen, was ihm erlaubt, zum Beispiel an Cookies oder andere Daten zu kommen.

Der klassische XSS Angriff zielte darauf ab, den Server dazu zu bringen, den bösartigen Code beim dynamischen Generieren des HTML Codes einzubauen. Bei WebApps gibt es häufig eine noch direktere Methode, den HTML DOM (Document Object Model) zu manipulieren. Man nützt aus, dass die WebApp selbst ihren DOM manipulieren kann. Ein XSS Angriff, der darauf abzielt, heisst DOM based XSS. Dies macht es möglich einen XSS Angriff durchzuführen, ohne dass die bösartige Eingabe via den Server gehen muss. So kann man einen solchen Angriff und die entsprechende Schwachstelle auch nicht mehr in den Server Logs finden.

DOM based XSS

Um eine Webapp vor XSS zu schützen, sollte man, wo immer möglich, Inputs von Benutzern, die irgendwo auf der Website dargestellt werden auf gefährliche Zeichen, wie zum Beispiel das Öffnen eines HTML Tags, untersuchen und diese encoden. Für DOM based XSS sollten sichere Manipulationen des DOMs verwendet werden. Zum Beispiel statt element.innerHTML sollte man element.innerText verwenden.

Es ist sehr schwierig alle XSS zu erkennen und zu verhindern, speziell in einer schon existierenden Applikation. In grossen WebApps kann man Mühe habe, alle Daten exakt zu verfolgen: woher sie stammen und wo sie eingesetzt werden.

XML Angriff

XML (Extensible Markup Language) ist eine Sprache, um Daten in ein von Menschen und Maschinen lesbares Format zu serialisieren. Dies erlaubt es, Daten zwischen verschiedenen Systemen auszutauschen. Neben der Serialisierung der Daten hat XML auch die Möglichkeit, das Format der Daten zu spezifizieren durch das Verwenden von Doctype und Element Tags:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE order [ <!ELEMENT account (#PCDATA)>
<!ELEMENT contact (#PCDATA)>
<!ELEMENT count (#PCDATA)>
<!ELEMENT order (product, count, orderer)>
<!ELEMENT orderer(contact, account)>
<!ELEMENT product (#PCDATA)>
]>
<order>
<product>1234</product> <count>1</count>
<orderer>
<contact>Jan P. Monsch</contact> <account>789</account>
</orderer>
</order>

Anstatt direkt im XML File, kann eine derartige Definition auch in einem externen File sein, was dann so aussieht:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE order SYSTEM "order.dtd">
<order>
<product>1234</product>

Neben der Definition der Datenstruktur kann man auch Werte vordefinieren und diese im Dokument referenzieren. Die Daten können sowohl lokal definiert oder von einem externen Ort geladen werden: 

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE order [
<!ENTITY product "1234">
<!ENTITY order SYSTEM "https://www.compass-security.com/order.xml" >
]>
<order>
<product>&product;</product>
<orderer>&orderer;</orderer >
</order>

All diese verschiedenen Funktionen sorgen dafür, dass ein XML Parser sehr viele Fähigkeiten besitzen muss und genau das kann für Angriffe ausgenutzt werden. Beispielsweise können falsche Files referenziert werden, welche dann in das Dokument eingebettet werden:

<!DOCTYPE request [
<!ENTITY include SYSTEM “file:///etc/passwd">
]>
<request>
<description>&include;</description>
...

Solange das File ein existierendes Textfile ist und der Prozess, in dem der Parser läuft, darauf Zugriff hat, wird dies vom Parser ausgewertet.

Aber auch ohne Zugriff auf externe Dokumente ist ein XML Parser noch nicht sicher. Es ist zum Beispiel möglich, mit einem bösartigen XML Dokument die Ressourcen auf einem Server aufzubrauchen. Wir können einen XML Parser etwa folgenden Ausdruck auswerten lassen:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE lol [
 <!ENTITY x "lol">
 <!ELEMENT lol (#PCDATA)>
 <!ENTITY x1 "&x;&x;&x;&x;&x;&x;&x;&x;&x;&x;">
 <!ENTITY x2 "& x1;&x1;&x1;&x1;&x1;&x1;&x1;&x1;&x1;&x1;">
 <!ENTITY x3 "&x2;&x2;&x2;&x2;&x2;&x2;&x2;&x2;&x2;&x2;">
 <!ENTITY x4 "&x3;&x3;&x3;&x3;&x3;&x3;&x3;&x3;&x3;&x3;">
 <!ENTITY x5 "&x4;&x4;&x4;&x4;&x4;&x4;&x4;&x4;&x4;&x4;">
 <!ENTITY x6 "&x5;&x5;&x5;&x5;&x5;&x5;&x5;&x5;&x5;&x5;">
 <!ENTITY x7 "&x6;&x6;&x6;&x6;&x6;&x6;&x6;&x6;&x6;&x6;">
 <!ENTITY x8 "&x7;&x7;&x7;&x7;&x7;&x7;&x7;&x7;&x7;&x7;">
 <!ENTITY x9 "&x8;&x8;&x8;&x8;&x8;&x8;&x8;&x8;&x8;&x8;">
]>
<lolz>&x9;</lolz>

Obwohl dieses XML Dokument selbst relativ klein ist, wird wegen der Referenzierungen x9 zu einem fast 3GB grossem String ausgewertet.

Wie auch bei den beiden bereits beschriebenen Angriffen kann man auch diese Art mit einem Validierungsschritt vor dem Parsen des XML Dokuments verhindern oder mindestens erschweren. In solch einer Validierung kann zum Beispiel geprüft werden, dass nur erlaubte Dokumente referenziert werden. Zusätzlich sollten im XML Parser alle Funktionalitäten abgeschaltet werden, welche man nicht braucht, wie etwa externe oder parametrisierte Entitäten. Falls möglich, sollte auch das Auswerten von Doctype Deklarationen deaktiviert werden.

Same Origin und Cross Origin Resource Sharing

Die Same Origin Policy verhindert, dass man auf Daten eines anderen Ursprungs zugreifen kann. Mit Daten vom selben Ursprung meint man Daten, welche vom selben Host mit dem gleichen Protokoll und auf dem gleichen Port geladen werden. Die Same Origin Policy wurde eingeführt, um sicherzustellen, dass ein bösartiges Skript auf einer Webseite nicht die Daten einer anderen Website auslesen kann. Es gibt aber wichtiges zu beachten. Selbst wenn Daten von unterschiedlichem Ursprung sind, werden diese trotzdem vom Browser angefragt und der Server wird sie auch liefern. Es ist die Aufgabe des Browsers sicherzustellen, dass dann das JS, welches die Abfrage gemacht hat, nicht auf die Daten zugreifen kann.

Es gibt ein paar wichtige Ausnahmen der Same Origin Policy:

  • Wenn die angefragten Daten ein Skript sind, das mit dem <script> geladen wird, dann bekommt das Skript dieselbe Origin wie die Webseite, in die das Skript eingebettet ist, und nicht die Origin, von der es geladen wurde. Somit hat das Laden eines fremden Skripts von einer anderen Origin immer zur Folge, dass dieses vollen Zugriff auf alle Daten auf der Webseite hat.
  • Eine zweite Ausnahme sind Websockets. Wenn man einen Websocket aufmacht, dann wird die Same Origin Policy ignoriert und die Daten werden immer zurückgegeben. Wenn man also nicht will, dass auf Daten von einem beliebigen Ursprung zugegriffen werden kann, ist es wichtig, mindestens den Ursprung der Anfrage zu prüfen, bevor man das Öffnen eines Websockets zulässt. Noch besser ist, eine nicht rein auf Cookies basierende Authentifizierung zu verwenden. Auch CSRF (Cross Site Request Forgery) Tokens können als Hilfe verwendet werden, damit nur erlaubte Webseiten Zugriff auf die Daten bekommen.

Die Same Origin Policy hat das Problem, dass sie in gewissen Fällen zu strickt ist. Manchmal möchte man dennoch zulassen, Daten eines anderen Ursprungs abzufragen. Ein solches Beispiel ist, wenn die API und die Webseite selbst sich auf unterschiedlichen Hosts befinden. Möchte die Website nun Daten von der API holen wird dies von der Single Origin Policy unterbunden. In diesem Fall müsste der Server der Website zuerst die Daten holen und diese dann an den Client weitersenden. Es ist aber besser, die Daten gleich auf dem Server zu haben. Also hat man eine Methode gesucht, wie man dieses Problem umgehen kann.

Die Methode nutzt aus, dass ein JS den gleichen Ursprung bekommt und  man somit auf die Rückgabe eines JS zugreifen kann. Die benötigten Daten werden in ein Skript verpackt und via «script» Tag geladen. Diese Methode wurde JSONP genannt. Das Problem dieses Ansatzes ist, dass jemand an Stelle des benötigten Daten-Skripts irgendein Skript zurückgeben kann, welches dann ausgeführt wird. Um diese Sicherheitslücke zu schliessen, hat man CORS (Cross Origin Resource Sharing) eingeführt. Heutzutage sollte man immer CORS und nie JSONP verwenden.

CORS ist eine Möglichkeit die Same Origin Policy gezielt und granular abzuschwächen. Hierfür kann der Server, welcher die Anfrage bearbeitet (im vorherigen Beispiel der Server der API), der Anfrage einen CORS Header mitgeben. In diesem ist festgehalten, welche Origins auf die Daten zugreifen dürfen. Für unser Beispiel würde das dann so aussehen:

HTTP/1.1 200 OK
Date: Mon, 01 Dec2008 00:23:45 GMT
Server: Apache/2.4
Access-Control-Allow-Origin: https://webshop.com

Der Header kann genau auf einen von drei verschieden Werten gesetzt werden:

  • Ein spezifischer Origin: gibt genau diesem Origin Zugriff
  • *, gibt allen Origins Zugriff
  • Null, gibt dem «null» Origin Zugriff

Listen von Domänen oder Platzhalter in der Domäne sind nicht erlaubt. Dennoch möchte man in gewissen Fällen auch mehreren Domänen Zugriff geben. Eine einfache Lösung ist, den Header auf * zu setzten. Dies führt aber dazu, dass man gar keine Zugriff-Kontrolle mehr hat. Die bessere Lösung, um mehreren Origins Zugriff zu geben, ist diese dynamisch zu generieren:

if(in_array($origin, $allowed)){
//origin matches -set ACAO header
header("Access-Control-Allow-Origin:$origin");
} else {
//origin does not match -don't set ACAO header
} 

Es ist wichtig, den Header ganz wegzulassen, wenn der Origin nicht zu den erlaubten Origins gehört. Denn man kann zu einfach eine Abfrage erstellen, welche eine null Origin hat.

Ein weiterer Vorteil von CORS ist, dass Cookies bei Cross-Origin Anfragen nicht mehr automatisch mitgesendet werden. Das Mitsenden von Cookies und allfällige andere Autorisierungsinformationen mit einer Cross-Origin Anfrage, muss beim Erstellen der Anfrage aktiviert werden:

xmlHttp.open('GET', 'https://api.webshop.com/profile', true);
xmlHttp.withCredentials= true;
xmlHttp.onreadystatechange= function() {
if(xmlHttp.readyState== 4) {
alert(xmlHttp.responseText);
}
};
xmlHttp.send(null);

Der Server hat dann die Möglichkeit im HTTP Header zu definieren, ob es erlaubt ist, Information mit Autorisierung abzufragen.

Access-Control-Allow-Origin: https://webshop.com
Access-Control-Allow-Credentials: true

Nur wenn der Access-Control-Allow-Credentials Header definiert ist und auf true gesetzt ist, kann der aufrufende Code darauf zugreifen. Der Server kann somit Zugriff auf öffentliche Daten einer Origin gewähren, aber Zugriff auf Daten, auf die man nur als angemeldeter Benutzer Zugriff hat, verweigern.

Im Vergleich zu den anderen hier präsentierten Themen ist CORS und Same Origin Policy nicht a priori eine Schwachstelle oder ein Angriffspunkt, sondern eine Möglichkeit, bei richtiger Konfiguration, Angriffe zu verhindern. Die Herausforderung ist, CORS sauber zu implementieren. Wenn CORS zu offen konfiguriert wird, sorgt es dafür, dass zu viele Origins auf die Daten zugreifen können. Wie im folgenden Beispiel, kann dies dazu verwendet werden, Daten aus einer geschützten Domäne zu extrahieren.

 Zu offen konfigurierter CORS 

Abschluss

Web Security ist ein sehr komplexes Thema. Es kann sehr einfach und auf vielfältige Weise zu Schwachstellen in einer Anwendung kommen. Mit der vermehrten Verwendung von Homeoffice und Bring-Your-Own-Device sind selbst interne Applikationen grösseren Gefahren ausgesetzt. Aus diesem Grund ist es beim Entwickeln von Anwendungen immer wichtig, die Sicherheit im Hinterkopf zu haben.

Für jene, die sich nebst den hier erwähnten Themen noch weiter mit der Sicherheit von Applikationen auseinandersetzen möchten, empfehle ich den ASVS (Application Security Verification Standard) der OWASP (Open Web Application Security Project) durchzulesen. Er bietet eine gute Übersicht über Sicherheitsvorkehrungen, die für eine Applikation getroffen werden sollten.

Alle, die an einem WebApp Security Workshop interessiert sind, dürfen sich gerne bei uns melden. Weitere Infos zu all unseren Workshop findet man unter: m-f.ch/mf-academy

0
Highlights vom .NET Day Switzerland 2022
Nach über 22 Jahren bei M&F in den wohlverdienten ...

Ähnliche Beiträge

 

Kommentare

Derzeit gibt es keine Kommentare. Schreibe den ersten Kommentar!
Montag, 29. April 2024

Sicherheitscode (Captcha)