Sicherheitslücken

OWASP

Sicherheitslücken sind meistens in der IT ein Tabu-Gesprächsthema. Oftmals wenn ich beruflich oder privat auf Foren oder sonstigen Plattformen unterwegs bin, stelle ich fest, dass das Thema IT-Sicherheit zwar mittlerweile doch weiter in den Vordergrund gerückt ist, aber nach wie vor noch ein recht grenz wertiges Wissen darüber besteht. Dabei werden rund alle drei Jahre von der OWASP (die denen es nichts sagt: Open Web Application Security Project) Statistiken über die häufigsten Sicherheitslücken in Anwendungen bereitgestellt.

Ignoranz oder Unwissenheit

Trotz der sorgfältigen Berichte der OWASP, merke ich das kaum Handlungsbedarf darin besteht, sich über diese Berichte zu informieren und präventive Maßnahmen gegen diese zu erstellen, oder gar bestehende Anwendungen auf genau diese Sicherheitslücken zu prüfen. Evtl. fehlt das Budget für das jeweilige Projekt, um diese Sicherheitslücken auszumerzen, jedoch ist ein Einbruch in eine Software letztlich immer mit einem finanziellen Schaden oder einem Imageschaden verbunden. Dahingehend sollte sich jeder selbst gut überlegen ob ihm das jeweilige Risiko Wert ist.

Transparenz schaffen

Die OWASP Top 10 sind einerseits Berichte, die Rückschlüsse aus den vergangenen und häufigsten Angriffen bringen. Meistens allerdings von der Beschreibung der Überprüfung recht mager formuliert.

Dahingehend möchte ich in den nächsten voraussichtlichen vier Teilen dieses Beitrags genauer auf die jeweiligen Schwachstellen eingehen, um ein Grundverständnis für die jeweilige Art der Sicherheitslücke zu vermitteln. Jegliche Informationen die Sie in diesem Beitrag vorfinden, dienen zu Schulungszwecken. Jeder ist für sein eigenes handeln selbst verantwortlich.

 

 OWASP TOP 10 – Sicherheitslücken

 Nr. 1: Injections

Injections zählen nach wie vor, seit mehreren Jahren, zu den häufigsten Angriffsmöglichkeiten, die Entwickler einem Angreifer in die Hand geben. Genauer genommen sogar die fatalsten.

Aber was genau ist nun eine Injection?

Auch dazu gibt es vieles zu sagen, es gibt Unterschiede zwischen Injection zu Injection, und widerum zur jeweiligen Vorgehensweise. Im Grunde sind sie alle, allerdings recht ähnlich, ob nun eine Injection gegen einen SQL-, LDAP-, NoSQL-Server erfolgt ist unabhängig. In allen Arten der genannten verschiedenen Datenbanken oder Verzeichnisdiensten lassen sich bei unsauberer Programmierung Sicherheitslücken implementieren. Diese Sicherheitslücken äußern sich in der Form, dass sich verschiedene Statements auf diese Server ausführen lassen können um nicht die gewünschten Daten zu manipulieren oder zu selektieren, sondern andere. Im nachfolgenden Teil gehe ich auf das weit verbreitete Beispiel mit einer MySQL-Datenbank ein.

 Implementierung

Es gibt verschiedene Wege einen Weg für eine SQL-Injection zu implementieren. Explizit gehe ich dabei auf einen recht bekannten Weg ein.

<?php

	require_once("path/to/config.inc.php");
	$SQL = "SELECT * FROM table WHERE id=".$_GET["id"];
	$res = mysql_query($SQL);
	foreach($row = mysql_fetch_assoc($res)){
		print_r($row);
	}

Dies ist eine beispielhafte Implementierung. Dabei könnte sich der Angriff wie folgt äußern. Nehmen wir an dieser Sourcecode liegt in einer index.php und ist vorzufinden auf example.com. Dann findet sich diese Datei unter dem direkten Pfad http://example.com/index.php

Wenn nun diese Datei mit folgenden Parametern angesprochen wird, kann ein Angreifer recht schnell erkennen ob eine SQL-Injection möglich ist, anhand eines geworfenenen PHP-Fehlers:

http://example.com/index.php?id=1'

Ein Fehler wird selbstverständlicherweise nur geworfen, wenn das Error_Reporting von PHP aktiviert ist, und display_errors auf on steht. Andererseits, würde die Möglichkeit der Injection erkannt werden können, anhand der nicht Interpretation der eigentlichen Daten.
Anschließend ließe sich z.B. über ein order by mit einem anschließenden union select eine Spalte ermitteln, welche zur Ausgabe von anderen Daten verwendet werden könnte.

Präventive Maßnahmen

Auch für SQL-Injections gibt es Möglichkeiten diese zu unterbinden. Die beste Möglichkeit dabei ist die Verwendung von Prepared Statements, oder bei nummerischen Werten die Verwendung von Type-Casting. Die Implementation beim Type-Casting ist recht überschaubar, nehmen wir dazu das obige Beispiel wieder auf.

 

<?php
	require_once("path/to/config.inc.php");

	$id = (int)$_GET["id"];

	$SQL = "SELECT * FROM table WHERE id='$id'";
	$res = mysql_query($SQL);
	foreach($row = mysql_fetch_assoc($res)){
		print_r($row);
	}

Damit wird erst einmal erzwungen, dass jegliche übermittelte Eingabe aus dem Get-Parameter „id“ in einen integer konvertiert wird. Wird ein String übermittelt, wird dieser aus gecasted. Daraus ergibt sich dann der Integer-Wert 0.

Eine andere, mehr effektivere Maßnahme ist die Verwendung von Prepared Statements, aber auch bei diesen, habe ich oftmals bereits eine fehlerhafte Implementierung gesehen. Was Sie nicht tun sollten, zeige ich Ihnen nun.

<?php
	require_once("path/to/config.inc.php");

	$id = $_GET["id"];

	$SQL = "SELECT * FROM table WHERE id='$id'";
	$sth = $dbh->prepare($SQL);
	$sth->execute();
	print_r($sth->fetchAll());

In diesem Beispiel wird zwar der PDO-Treiber für die SQL-Statements mit der prepare-Methode verwendet, führt allerdings angesichts der Implementation nicht zu dem gewünschten Ergebnis. Das Statement wird zwar vorbereitet, aber der PDO-Treiber wird an der Stelle das SQL-Statement nicht durchfiltern, da nicht angegeben ist, wonach gefiltert werden muss.

Die richtige Implementierung dessen wäre die folgende:

<?php
	require_once("path/to/config.inc.php");

	$id = $_GET["id"];

	$SQL = "SELECT * FROM table WHERE id= :id";
	$sth = $dbh->prepare($SQL);
	$sth->bindParam(":id", $id, PDO::PARAM_INT);
	$sth->execute();
	print_r($sth->fetchAll());

In diesem Fall geben Sie ein Schema wie das SQL-Statement auszusehen hat vor, und lassen es vom PDO-Treiber vorbereiten. Nach der Vorbereitung erhalten Sie ein PDO-Statement-Object zurück. Diesem Statementobjekt vermitteln Sie über die „bindParam“-Methode, welcher Platzhalter mit welchem Wert befüllt werden muss und welchem Datentypen dieser Platzhalter wiederum entsprechen muss.
Bei der Verwendung von Insert oder Update Statements sollten Sie des weiteren ebenfalls andere Inhalte rausfiltertn, wie z.B. HTML-Tags, z.B. durch die Verwendung von htmlspecialchars, ansonsten laufen Sie in andere Sicherheitslücken rein, auf welches ich im nächsten Artikel eingehen werde.