Hi zusammen,
da die Sicherheit im Internet derzeit ein brandheißes Thema ist, wollte ich an dieser Stelle eine kleine Einführung zur Websicherheit zusammenstellen, die die wichtigsten und häufigsten Schwachstellen in (hoffentlich) verständlicher Form vorstellt.
Dafür habe ich mir folgende Themen überlegt:
Durchgestrichene Themen folgen noch, die einzelnen Abschnitte werde ich nach und nach schreiben, wenn ich die Zeit dazu finde.
Logical Flaws
Was sind Logical Flaws überhaupt?
Ich besuche die Seite eines kleinen Shops für Schreibwaren, der derzeit eine Rabattaktion führt, bei der ab einer Bestellung im Wert von 25€ 5€ Rabatt gewährt werden, wenn der Gutscheincode eingegeben wird. Die Gelegenheit ist ideal, um meine Kugelschreibersammlung zu erweitern, also besuche ich den Shop und lege das gute Stück in den Warenkorb. Ich betrachte diesen nun und finde auch prompt das Feld zur Eingabe des Rabattcodes. Doch da mein Kugelschreiber nur 5,99€ kostet, verwehrt mir der Shop, den Gutscheincode einzulösen.
Zu blöd nur, dass ich den Kugelschreiber unbedingt will, aber mir kommt die Idee, meinen Warenkorb solange mit Artikeln zu füllen, bis der Gesamtwert über 25€ liegt. Erneut gebe ich den Gutscheincode ein. Dieses mal wird er akzeptiert und ich sehe im Warenkorb meine Artikel aufgelistet. Darunter eine Bemerkung, dass ein 5€-Gutschein aktiv ist.
Leider stelle ich fest, dass mein Monatsbudget für Kugelschreiber schon fast aufgebraucht ist und nur Geld für den ursprünglichen Kugelschreiber habe. Also entferne ich kurzerhand alle Artikel außer diesem aus dem Warenkorb. Beim nächsten Schritt zur Kasse entdecke ich zu meiner Überraschung, dass mir für den Kugelschreiber, der ursprünglich doch 5,99€ kostete, nur 99ct angerechnet werden.
Was ist hier passiert?
Obwohl es in dem Code des Shops keine keine Bugs im eigentlichen Sinn gibt, die ich ausnutzen könnte, ist es mir gelungen, einen Gutschein einzulösen, der erst ab einem Warenwert von 25€ genutzt werden dürfte. Die Entwickler haben bedacht, dass beim Einlösen des Gutscheins der Warenwert aller Artikel im Warenkorb größer als 25€ sein muss, doch vergaßen sie, dass Artikel auch wieder entfernt werden können und die Gültigkeit des Gutscheins dann erneut geprüft werden sollte. Da es sich bei allen Schritten um legitime Aktionen handelt, die von Programmierern vorgesehen und implementiert wurden, ist der Angriff schwer zu erkennen. Insbesondere automatische Tools wie Intrusion Detection Systems werden keinen Alarm schlagen, da schädliche Anfragen nicht von Unschädlichen unterscheidbar sind. Hier ist also nicht nur sichere Programmierung notwendig, sondern müssen Abfolgen von Interaktionen bei der Entwicklung des Shops berücksichtigt werden, um sich effektiv zu schützen.
Ein Paket Tee und 15 Schweizer Franken bitte
Wer kennt das nicht: Man sitzt gemütlich auf der Couch und genießt seinen Nachmittagstee als einem der Gedanke kommt, "Und jetzt noch für's Tee trinken bezahlt werden". Gesagt, getan! Nach einer kurzen Recherche im Internet bin ich auf einen Onlineshop für Tee gestoßen, bei dem ich mir meine eigene Mischung aus einer Liste von gegebenen Zutaten zusammenstellen kann.
Doch nicht nur die Zutaten kann ich zusammenstellen. Es scheint, mit der Anzahl der Zutaten ändert sich auch der Preis. Das ist ein genaueres Hinschauen wert. Tatsächlich werde ich im Quellcode der Seite fündig und entdecke folgenden Code-Ausschnitt:
[src=html4strict]<input type="hidden" name="preis" id="preisinput">
<input type="hidden" name="hot" id="hotinput">
<input type="hidden" name="intense" id="intenseinput">
<input type="hidden" name="sweet" id="sweetinput">[/src]
... nicht wirklich, oder? Doch.
Ich denke, die Lösung des Problems versteht sich an dieser Stelle von selbst: Der Preis sollte serverseitig anhand der Zutaten berechnet werden.
Der 0x-Trick
Gut, solche Tricks wie bei dem Tee-Shop gibt es nur noch selten zu finden - und wenn, dann werden sie meistens nach sehr kurzer Zeit ausgenutzt, fast behoben, nochmal ausgenutzt, und dann wirklich behoben. Deutlich unbekannter ist jedoch der 0x-Trick.
Für diesen Abschnitt habe ich leider kein Beispiel zum Anschauen parat, dafür aber ein wenig eigenen Code, der vereinfacht das Kennwort-Ändern Formular einer kleinen Seite entgegen nimmt:
[src=php]$password = $_POST['password'];
$userid = $_POST['uid'];
if($userid == 0) {
// admin user has id 0, don't change password
} else {
// change password
$sql = 'update users set password = "' . md5($password) . '" where id = ' . intval($userid);
// execute sql and stuff
}[/src]
Wenn man davon absieht, dass md5 ohne Salt für Kennworthashes genutzt wird, sieht das auf den ersten Blick recht sicher aus. Der Admin hat ID 0 und nur für andere Nutzer kann das Kennwort geändert werden. Oder?
PHP hat bekanntlich ein paar Macken, gerade was die Sicherheit angeht. Eine davon ist hier zu finden: Das Casten zu einem Integer passiert auf zwei verschiedene Wege.
Einmal gibt es einen impliziten Cast in der if-Bedingung und ein weiterer wird von intval() ausgeführt. Ist die Eingabe ein String, der eine Zahl in Hex-Repräsentation darstellt, also beispielsweise '0x1234', so wird dieser String in der if-Bedingung zum Integer 4660 gecastet und ist offensichtlich ungleich 0. intval() hingegen geht ein wenig anders vor: Die Funktion sucht im Eingabestring nach Zeichen zwischen '0' und '9', d.h. nach Ziffern. Wird ein Buchstabe gefunden, so wird die Zahl an dieser Stelle abgeschnitten:
intval('123') == 123
intval('12k3') == 12
intval('abc') == 0
Dreimal dürft ihr raten, was passiert, wenn der Eingabestring '0x1' ist.
Wir haben also die if-Abfrage gebypassed und können nun das Admin-Kennwort ändern, obwohl dies im Code eigentlich ausschließlich zu unterbinden versucht wird.
Besser wäre es gewesen, wenn zusätzlich zu Beginn des Scripts intval direkt auf $_POST['uid'] angewendet worden wäre, da zu keinem Zeitpunkt im Script etwas anderes als eine Zahl erwartet wurde:
[src=php]$userid = intval($_POST['uid']);[/src]
Was sonst noch? Information Leak.
Logical Flaws sind bisher als eher kleinere Fehler aufgefallen, die recht einfach zu beheben sind. Problematischer wird es allerdings, wenn mittels Logical Flaws Informationen über den Server geleaked werden können, die einem böswilligen Hacker weitere Angriffe erleichtern. So wäre es denkbar, dass Local File Inlcusions (dazu später mehr) möglich sind, über die ein Angreifer Dateien auslesen kann, die nicht für die Öffentlichkeit bestimmt sind. Interessant ist beispielsweise das Webroot-Verzeichnis, um von dort systematisch nach bestimmten Dateien zu suchen.
An diese Informationen kann ein Angreifer kommen, indem er absichtlich Fehlermeldungen provoziert, die in PHP teilweise als Warnungen, teilweise als Hinweise und teilweise als kritische Fehler ausgegeben werden. Eine Warnung kann beispielsweise provoziert werden, indem ein Parameter, welcher in den Funktionsaufruf mysql_real_escape_string wandert, in der URL zu einem Array gemacht wird:
Um diese Problematik zu verhindern, sollten (technische) Fehlermeldung im Allgemeinen nur für die Entwicklung zugelassen werden, im Produktivbetrieb sollten diese jedoch abgeschaltet werden.
[src=php]error_reporting(0);[/src]
Doch auf diese Weise wird nur das Symptom behoben. Besser ist es, alle Werte und Parameter, die nicht vom Script selbst verwaltet werden, auf ihren Typ zu untersuchen, bevor diese verwendet werden. In PHP gibt es zu diesem Zweck die Funktionen isset, is_string, is_array, is_int, is_bool, is_float, is_decimal und viele Weitere. Wie im Fall einer falschen Eingabe vorzugehen ist, hängt von der Anwendung ab: manchmal genügt es, einen Standardwert stattdessen zu setzen, manchmal muss die Ausführung des Scripts abgebrochen werden.
Die robots.txt
Die robots.txt ist eine Datei, die Bots und Crawler anweist, welche Verzeichnisse durchsucht werden sollen und welche nicht. Die Angaben sind für Bots nicht verpflichtend und können umgekehrt Informationen über die Verzeichnisstruktur unter dem Webroot geben. Ist beispielsweise ein Eintrag vorhanden, der den Zugriff auf den Ordner /a82_secret_login_page verbietet, kann dieses Wissen eingesetzt werden, um eben den Pfad zum Login in Erfahrung zu bringen.
Als Alternative könnte der Eintrag in der robots.txt weggelassen werden und stattdessen in den HTML-Dokumenten unter diesem Pfad ein meta-Tag gesetzt werden, der Suchmaschinen die Indizierung verbietet:
[src=html4strict]<meta name="robots" content="noindex,nofollow">[/src]
Zusammenfassung
Bei der Entwicklung von Webanwendungen immer prüfen
Ich hoffe, dieser Text ist einigermaßen verständlich. Bei Unklarheiten beantworte ich sonst aber gerne Fragen. Kritik und Verbesserungsvorschläge sind immer erwünscht, insbesondere wenn ein ganzer Block im Text unklar/unschön/falsch ist. An dieser Stelle möchte ich auch anmerken, dass ich keine Gewähr für die Richtigkeit meiner Aussagen übernehme; Fehler können auch mir passieren.
Weitere Informationen zum Thema von anderen Usern
https://ngb.to/threads/17590-Guide-Sichere-Webanwendungen-erstellen?p=561125#post561125
Ich Gedenke bis zum Wochenende Anfang August (private Projekte sind Grund für Verspätung) das Thema Remote Code Execution zu posten.
Anmerkung 1: Mittlerweile hat der Betreiber des Tee-Shops das Problem erkannt und setzt bei negativem Preis den Gesamtpreis schlicht auf 0 CHF.
Anmerkung 2: Ich habe den Text vorm Posten kein zweites Mal gelesen. Yoloswag.
da die Sicherheit im Internet derzeit ein brandheißes Thema ist, wollte ich an dieser Stelle eine kleine Einführung zur Websicherheit zusammenstellen, die die wichtigsten und häufigsten Schwachstellen in (hoffentlich) verständlicher Form vorstellt.
Dafür habe ich mir folgende Themen überlegt:
- Logical Flaws
- Cross Site Sripting (XSS)
- SQL Injection (SQLi)
- Local/Remote File Inclusion (LFI/RFI)
- Remote Code Execute (RCE)
- Cross Site Request Forgery (CSRF/XSRF)
- Seitenkanäle
Durchgestrichene Themen folgen noch, die einzelnen Abschnitte werde ich nach und nach schreiben, wenn ich die Zeit dazu finde.
Logical Flaws
Was sind Logical Flaws überhaupt?
Ich besuche die Seite eines kleinen Shops für Schreibwaren, der derzeit eine Rabattaktion führt, bei der ab einer Bestellung im Wert von 25€ 5€ Rabatt gewährt werden, wenn der Gutscheincode eingegeben wird. Die Gelegenheit ist ideal, um meine Kugelschreibersammlung zu erweitern, also besuche ich den Shop und lege das gute Stück in den Warenkorb. Ich betrachte diesen nun und finde auch prompt das Feld zur Eingabe des Rabattcodes. Doch da mein Kugelschreiber nur 5,99€ kostet, verwehrt mir der Shop, den Gutscheincode einzulösen.
Zu blöd nur, dass ich den Kugelschreiber unbedingt will, aber mir kommt die Idee, meinen Warenkorb solange mit Artikeln zu füllen, bis der Gesamtwert über 25€ liegt. Erneut gebe ich den Gutscheincode ein. Dieses mal wird er akzeptiert und ich sehe im Warenkorb meine Artikel aufgelistet. Darunter eine Bemerkung, dass ein 5€-Gutschein aktiv ist.
Leider stelle ich fest, dass mein Monatsbudget für Kugelschreiber schon fast aufgebraucht ist und nur Geld für den ursprünglichen Kugelschreiber habe. Also entferne ich kurzerhand alle Artikel außer diesem aus dem Warenkorb. Beim nächsten Schritt zur Kasse entdecke ich zu meiner Überraschung, dass mir für den Kugelschreiber, der ursprünglich doch 5,99€ kostete, nur 99ct angerechnet werden.
Was ist hier passiert?
Obwohl es in dem Code des Shops keine keine Bugs im eigentlichen Sinn gibt, die ich ausnutzen könnte, ist es mir gelungen, einen Gutschein einzulösen, der erst ab einem Warenwert von 25€ genutzt werden dürfte. Die Entwickler haben bedacht, dass beim Einlösen des Gutscheins der Warenwert aller Artikel im Warenkorb größer als 25€ sein muss, doch vergaßen sie, dass Artikel auch wieder entfernt werden können und die Gültigkeit des Gutscheins dann erneut geprüft werden sollte. Da es sich bei allen Schritten um legitime Aktionen handelt, die von Programmierern vorgesehen und implementiert wurden, ist der Angriff schwer zu erkennen. Insbesondere automatische Tools wie Intrusion Detection Systems werden keinen Alarm schlagen, da schädliche Anfragen nicht von Unschädlichen unterscheidbar sind. Hier ist also nicht nur sichere Programmierung notwendig, sondern müssen Abfolgen von Interaktionen bei der Entwicklung des Shops berücksichtigt werden, um sich effektiv zu schützen.
Ein Paket Tee und 15 Schweizer Franken bitte
Wer kennt das nicht: Man sitzt gemütlich auf der Couch und genießt seinen Nachmittagstee als einem der Gedanke kommt, "Und jetzt noch für's Tee trinken bezahlt werden". Gesagt, getan! Nach einer kurzen Recherche im Internet bin ich auf einen Onlineshop für Tee gestoßen, bei dem ich mir meine eigene Mischung aus einer Liste von gegebenen Zutaten zusammenstellen kann.
Doch nicht nur die Zutaten kann ich zusammenstellen. Es scheint, mit der Anzahl der Zutaten ändert sich auch der Preis. Das ist ein genaueres Hinschauen wert. Tatsächlich werde ich im Quellcode der Seite fündig und entdecke folgenden Code-Ausschnitt:
[src=html4strict]<input type="hidden" name="preis" id="preisinput">
<input type="hidden" name="hot" id="hotinput">
<input type="hidden" name="intense" id="intenseinput">
<input type="hidden" name="sweet" id="sweetinput">[/src]
... nicht wirklich, oder? Doch.
Ich denke, die Lösung des Problems versteht sich an dieser Stelle von selbst: Der Preis sollte serverseitig anhand der Zutaten berechnet werden.
Der 0x-Trick
Gut, solche Tricks wie bei dem Tee-Shop gibt es nur noch selten zu finden - und wenn, dann werden sie meistens nach sehr kurzer Zeit ausgenutzt, fast behoben, nochmal ausgenutzt, und dann wirklich behoben. Deutlich unbekannter ist jedoch der 0x-Trick.
Für diesen Abschnitt habe ich leider kein Beispiel zum Anschauen parat, dafür aber ein wenig eigenen Code, der vereinfacht das Kennwort-Ändern Formular einer kleinen Seite entgegen nimmt:
[src=php]$password = $_POST['password'];
$userid = $_POST['uid'];
if($userid == 0) {
// admin user has id 0, don't change password
} else {
// change password
$sql = 'update users set password = "' . md5($password) . '" where id = ' . intval($userid);
// execute sql and stuff
}[/src]
Wenn man davon absieht, dass md5 ohne Salt für Kennworthashes genutzt wird, sieht das auf den ersten Blick recht sicher aus. Der Admin hat ID 0 und nur für andere Nutzer kann das Kennwort geändert werden. Oder?
PHP hat bekanntlich ein paar Macken, gerade was die Sicherheit angeht. Eine davon ist hier zu finden: Das Casten zu einem Integer passiert auf zwei verschiedene Wege.
Einmal gibt es einen impliziten Cast in der if-Bedingung und ein weiterer wird von intval() ausgeführt. Ist die Eingabe ein String, der eine Zahl in Hex-Repräsentation darstellt, also beispielsweise '0x1234', so wird dieser String in der if-Bedingung zum Integer 4660 gecastet und ist offensichtlich ungleich 0. intval() hingegen geht ein wenig anders vor: Die Funktion sucht im Eingabestring nach Zeichen zwischen '0' und '9', d.h. nach Ziffern. Wird ein Buchstabe gefunden, so wird die Zahl an dieser Stelle abgeschnitten:
intval('123') == 123
intval('12k3') == 12
intval('abc') == 0
Dreimal dürft ihr raten, was passiert, wenn der Eingabestring '0x1' ist.
lol, für sowas lohnt sich ein Spoiler nun echt nicht.
Na gut: intval('0x1') == 0
Wir haben also die if-Abfrage gebypassed und können nun das Admin-Kennwort ändern, obwohl dies im Code eigentlich ausschließlich zu unterbinden versucht wird.
Besser wäre es gewesen, wenn zusätzlich zu Beginn des Scripts intval direkt auf $_POST['uid'] angewendet worden wäre, da zu keinem Zeitpunkt im Script etwas anderes als eine Zahl erwartet wurde:
[src=php]$userid = intval($_POST['uid']);[/src]
Was sonst noch? Information Leak.
Logical Flaws sind bisher als eher kleinere Fehler aufgefallen, die recht einfach zu beheben sind. Problematischer wird es allerdings, wenn mittels Logical Flaws Informationen über den Server geleaked werden können, die einem böswilligen Hacker weitere Angriffe erleichtern. So wäre es denkbar, dass Local File Inlcusions (dazu später mehr) möglich sind, über die ein Angreifer Dateien auslesen kann, die nicht für die Öffentlichkeit bestimmt sind. Interessant ist beispielsweise das Webroot-Verzeichnis, um von dort systematisch nach bestimmten Dateien zu suchen.
An diese Informationen kann ein Angreifer kommen, indem er absichtlich Fehlermeldungen provoziert, die in PHP teilweise als Warnungen, teilweise als Hinweise und teilweise als kritische Fehler ausgegeben werden. Eine Warnung kann beispielsweise provoziert werden, indem ein Parameter, welcher in den Funktionsaufruf mysql_real_escape_string wandert, in der URL zu einem Array gemacht wird:
Um diese Problematik zu verhindern, sollten (technische) Fehlermeldung im Allgemeinen nur für die Entwicklung zugelassen werden, im Produktivbetrieb sollten diese jedoch abgeschaltet werden.
[src=php]error_reporting(0);[/src]
Doch auf diese Weise wird nur das Symptom behoben. Besser ist es, alle Werte und Parameter, die nicht vom Script selbst verwaltet werden, auf ihren Typ zu untersuchen, bevor diese verwendet werden. In PHP gibt es zu diesem Zweck die Funktionen isset, is_string, is_array, is_int, is_bool, is_float, is_decimal und viele Weitere. Wie im Fall einer falschen Eingabe vorzugehen ist, hängt von der Anwendung ab: manchmal genügt es, einen Standardwert stattdessen zu setzen, manchmal muss die Ausführung des Scripts abgebrochen werden.
Die robots.txt
Die robots.txt ist eine Datei, die Bots und Crawler anweist, welche Verzeichnisse durchsucht werden sollen und welche nicht. Die Angaben sind für Bots nicht verpflichtend und können umgekehrt Informationen über die Verzeichnisstruktur unter dem Webroot geben. Ist beispielsweise ein Eintrag vorhanden, der den Zugriff auf den Ordner /a82_secret_login_page verbietet, kann dieses Wissen eingesetzt werden, um eben den Pfad zum Login in Erfahrung zu bringen.
Als Alternative könnte der Eintrag in der robots.txt weggelassen werden und stattdessen in den HTML-Dokumenten unter diesem Pfad ein meta-Tag gesetzt werden, der Suchmaschinen die Indizierung verbietet:
[src=html4strict]<meta name="robots" content="noindex,nofollow">[/src]
Zusammenfassung
Bei der Entwicklung von Webanwendungen immer prüfen
- wer welche Informationen sehen kann und darf.
- auf welche Variablen ein potentieller Angreifer Einfluss hat.
- typsicher programmieren.
- die Gesamtlogik einer Webanwendung berücksichtigen.
Ich hoffe, dieser Text ist einigermaßen verständlich. Bei Unklarheiten beantworte ich sonst aber gerne Fragen. Kritik und Verbesserungsvorschläge sind immer erwünscht, insbesondere wenn ein ganzer Block im Text unklar/unschön/falsch ist. An dieser Stelle möchte ich auch anmerken, dass ich keine Gewähr für die Richtigkeit meiner Aussagen übernehme; Fehler können auch mir passieren.
Weitere Informationen zum Thema von anderen Usern
https://ngb.to/threads/17590-Guide-Sichere-Webanwendungen-erstellen?p=561125#post561125
Ich Gedenke bis zum Wochenende Anfang August (private Projekte sind Grund für Verspätung) das Thema Remote Code Execution zu posten.
Anmerkung 1: Mittlerweile hat der Betreiber des Tee-Shops das Problem erkannt und setzt bei negativem Preis den Gesamtpreis schlicht auf 0 CHF.
Anmerkung 2: Ich habe den Text vorm Posten kein zweites Mal gelesen. Yoloswag.
Zuletzt bearbeitet: