[Tutorial] Sicherheit in PHP mit MySQL (Part 1: SQL-Injections)

02/23/2012 21:01 NullPointerException#1
Hallo,
ich dachte mir, mal ein Tutorial über Sicherheit in PHP-Skripten zu machen.
Na dann fang ich wohl an^^

Vorraussetzung ist PHP & MySQL zu können.

SQL-Injections sind der Albtraum eines jeden Webmasters.
Doch was ist eine SQL-Injection?
Wie der Name schon sagt wird SQL-Code in deinen SQL-Code eingeschleust.
Um das zu demonstrieren hier ein PHP-Script. NICHT BENUTZEN! BEINHALTET SICHERHEITSLÜCKE
PHP Code:
<?php
    
//Wir nehmen an per POST wurde ein Kennwort an dieses Skript gesendet, 
    //es befindet sich im Element mit dem Key 'unsicher'
    
$unsicher $_POST['unsicher'];
    
    
//Das Kennwor überprüfen wir nun in der DB und schauen ob es vorhanden ist.
    
$q mysql_query("SELECT * FROM `users` WHERE `kennwort` = '".$unsicher."'");
    
    
//Nun behandeln wir den user entsprechend.
    
if (mysql_num_rows($q) != 0) {
        echo 
'Eingeloggt!';
    } else {
        echo 
'Nix da!';
    }
?>
So. Sieht auf den ersten Blick ja nicht so falsch aus.
Doch: Jetzt kommt User XYZ, und probiert aus Langeweile das aus: er gibt
Code:
' OR 1=1 --
ein.

Was ist aus unserer Query geworden?

Richtig,
Code:
SELECT * FROM `users` WHERE `kennwort` = '' OR 1=1 --'
Da -- für ein Zeilenkommentar in SQL steht, interpretiert MySQL dies:

Code:
SELECT * FROM `users` WHERE `kennwort` = '' OR 1=1
Und das liefert alle Einträge der Tabelle zurück.
Wirklich alle.
Und somit ist
Code:
mysql_num_rows($q) != 0
(außer wenn die Tabelle leer ist) automatisch erfüllt - der User ist drin, ohne das Kennwort zu kennen.

Um dem vorzubeugen sollten wir die Usereingaben entsprechend escapen, also sichern.

Dies geht mit
PHP Code:
$string mysql_real_escape_string($input); 
für Strings, mit
PHP Code:
$int intval($input); 
für Integers.

Das ganze kann man dann in eine Funktion bauen, wie hier:
PHP Code:
<?php
    
function escapeMe($data$isInt false)
    {
        if (
$isInt)
            return 
intval($data);
        return 
mysql_real_escape_string($data);
    }
?>
Dann können wir integers so:
PHP Code:
$int escapeMe($inputtrue); 
und Strings so:
PHP Code:
$int escapeMe($input); 
escapen.

Und somit seid ihr bei korrekter Verwendung die SQL-Injection los.

Part2 (Ausgabesicherheit in Nachrichtenstrukturen) folgt!

NullPointerException
02/24/2012 01:31 Fratyr#2
Wieso eine eigene Funktion bauen? Wenn wir ein Eingabefeld haben in dem nur Zahlen erlaubt
werden sollen prüfen wir das mit is_int(), is_float() etc. den Datentyp deiner Value einfach
umzuwandeln ist vollkommen blödsinnig. Baust du deine Logins immer so? Wenn ja dann solltest du
auch darauf achten das auch der richtige User selektiert wird, ansonsten würde mysql_fetch_* alle User
enthalten die das selbe Passwort besitzen.

Btw ist es klüger die Usereingaben auf korrekten Dateityp bzw. erlaubte Zeichen zu prüfen, und falls
was falsch ist überhaubt keinen Query zu starten, das verhindert die Möglichkeit eine SQL
Injection durchzuführen komplett.

Quote:
Und somit seid ihr bei korrekter Verwendung die SQL-Injection los.
Nein eben nicht, ich werde hier die Diskussion die ich mit jemanden hier mal hatte nicht wieder
anfachen, aber mysql_real_escape_string bietet im Gegensatz zu Whitelists keine 100%ige Sicherheit
gegen SQL Injections.
02/24/2012 15:07 mydoom#3
Oder man hält potenziell unsichere Daten vom SQL-Interpreter fern und nutzt Prepared Statements
02/25/2012 10:11 JacK le chilla#4
Also für Anfänger ist das Tutorial doch ganz gut. Fortgeschrittene meckern doch einfach immer ;) Ja, stimmt 100%ig sicher ist mysql_real_escape_string nicht wer aber sich zutraut eigene preg_matches zu schreiben die richitg validieren sollte diese auch prüfen lassen und meine geb ich nicht raus ^^
02/25/2012 18:32 NullPointerException#5
Eine Whitelist ist auch nicht immer gut, zb wenn du in einem Forum ne suche hast und nach der SQL-Query "DROP TABLE ..." suchen würdest.
Ich nutze Prepared Statements, aber wie gesagt, ein Anfänger würde sich da denken: "ääääh was??"
Und ein bisschen Sicherheit ist besser als keine.
Und das mit dem alle user mit gleichem passwort werden ausgewählt:
Naja, das hier habe ich für Übungszwecke geändert.
Außerdem salte ich meine Passwörter mit einem (meines Erachtens) erweiterten algorithmus und deswegen ist das eigentlich eh unmöglich (praktisch gesehen)
das es zwei gleiche gibt^^
02/25/2012 18:48 Fratyr#6
Doch, whitelists sind immer gut. Würde wie in deinem Beispiel der User nach DROP TABLE suchen
würde es den SQL Query nicht beinflussen, da man htmlspecialchars anwenden würde damit alle
Zeichen im Suchstring verwendet werden können. mysql_real_escape_string könnte man hier
definitiv nicht verwenden.

Quote:
Außerdem salte ich meine Passwörter mit einem (meines Erachtens) erweiterten algorithmus und deswegen ist das eigentlich eh unmöglich (praktisch gesehen)
das es zwei gleiche gibt^^
Wenn du als Dev so ne Einstellung hast, solltest du die Programmierung wohl sein lassen. Egal
wie "erweitert" du deine Passwörter salzt, du musst dich auch gegen praktisch unmögliche
Dinge gewappnet sein, sollange es in der Theorie, auch wenn es um 1. Millionen
Ecken gedacht wäre möglich ist.

Quote:
Und ein bisschen Sicherheit ist besser als keine.
Theoretisch ja, praktisch nein. Wer wirklich darauf aus ist deine Website zu "hacken" wird nach einem
Versuch nicht locker lassen und es mit was anderem versuchen. Ein bisschen Sicherheit, ist keine
Sicherheit.