PDO Prepared Statements SQL Injection

  • PHP

Es gibt 6 Antworten in diesem Thema. Der letzte Beitrag () ist von 3daycliff.

    PDO Prepared Statements SQL Injection

    Guten Tag,

    habe eine kurze Frage. Habe zwar schon gegoogled, aber der eine sagt es so, der andere wieder anders. Daher stelle ich hier die Frage, in der Hoffnung, dass ich hier eine verständlichere Antwort erhalte.

    Es geht um PHP und PDO in Verbindung mit Prepared Statements.

    Hier mal ein Codebeispiel:

    PHP-Quellcode

    1. $username = $_POST['username'];
    2. $post_pw = $_POST['password'];
    3. $getCredentials = $db->prepare("SELECT pw_hash,salt FROM Accounts WHERE username=:username");
    4. $getCredentials->bindValue(':username', $username, PDO::PARAM_STR);
    5. $getCredentials->execute();


    Ist das schon sicher genug, um SQL-Injection zu verhindern?
    Laut der PDO-Dokumentation wird der String automatisch escaped.

    Wäre es schlimm, dies manuell nochmal zu machen? Wird das dann doppelt escaped? Entsteht daraus eine Gefahr?
    Also so etwas hier:

    PHP-Quellcode

    1. $username = $db->quote($_POST['username']);
    2. $post_pw = $db->quote($_POST['password']);
    3. $getCredentials = $db->prepare("SELECT pw_hash,salt FROM Accounts WHERE username=:username");
    4. $getCredentials->bindValue(':username', $username, PDO::PARAM_STR);
    5. $getCredentials->execute();


    Über hilfreiche Antworten wäre ich sehr dankbar!
    Wär halt unvorteilhaft. Weil's beim Auslesen (SELECT * ...) ja dann falsch rauskommt. stackoverflow.com/questions/22…-statements-of-pdo-in-php Ich nehm immer PDO::quote weil ich's einfach intuitiver und angenehmer finde, wobei beides gleich gut ist (Prepared Statements oder manuelles escapen). Der Vorteil von PS is halt dass man's nicht "vergessen" kann.

    Link :thumbup:
    Hello World
    Laut der PDO-Dokumentation wird der String automatisch escaped.

    Ich hoffe das steht so NICHT in der Doku. (Es sei dann du verwendest ATTR_EMULATE_PREPARES.) Bei Prepared Statment wird nichts escaped. Die Datebank parst die Abfrage schon bevor es die Parameter erhält und braucht deshalb gar nichts zu escapen, weil der SQL-String gar nicht in Berührung mit den Werten kommt.
    Damit hat man dann gar nicht die Gefahr von SQL-Injections.
    Beim esccapen (z.B. durch PDO::quote) besteht, wie Link schon schrieb, immer die Gefahr, dass man es irgendwann mal vergisst. ("Hey, das ist ein integer, den muss ich nicht escapen!"... Monate später landet doch mal ein String drin, man denk nicht dran (oder jemand anders programmiert am Projekt weiter) und im Worst-Case merkt man es erst, wenn jemand Unfug gemacht hat).

    Ein weiterer Vorteil von Prepared Statement ist, dass die DB die Abfrage cachen kann und bei mehrmaliger Ausführung sich so ein Performance-Gewinn einstellt. Z.B. bei solchen Schleifen zum Einfügen mehrerer Sätze:

    PHP-Quellcode

    1. <?php
    2. $stmt = $db->prepare('INSERT INTO foo (bar, baz) VALUES (:bar, :baz)');
    3. while(/*...*/) {
    4. // ...
    5. $stmt->bindValue(':foo', ...);
    6. $stmt->bindValue(':foo', ...);
    7. $stmt->execute();
    8. }


    Im Gegensatz zu Link würde ich dir raten, quote zu vermeiden.
    Es löst nicht das Kernproblem bei SQL-Injections (Vermischung von Code und Daten) und könnte trotz korrekter Verwendung zu Problemen führen. Gab irgendwo mal einen Artikel, der beschrieb, dass es unter ganz bestimmten Konstellation von Encoding, DB-Collation, MySQL-Version etc. möglich war, trotz korrekter Verwendung von mysql_real_escape_string, eine SQL-Injection durchzuführen. Etwas ähnliches könnte vielleicht bei quote auch mal passieren.
    Die Chance das so etwas bei Prepared Statments passiert ist wesentlich kleiner.

    Aber zur eigentlichen Frage: ;)
    Du solltest die Parameter NICHT vorher escapen. Benutze Prepared Statments und verwende für ALLE Variablen die Platzhalter in Verbindung mit bindValue und co.
    Beim Ausgaben von ALLEN Variablen dann aber noch durchgängig htmlspecialchars verwenden.

    Und zum Punkto Sicherheit: Zum Hashen und Überprüfen von Passworten die password_hash und password_verify verwenden.
    Ich meine gelesen zu haben, man soll die emulierten Prepared Statements unbedingt ausschalten.
    $this->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
    Gut, danke. Dann werde ich das escapen vorher lassen.
    Bezüglich der Doku war das so gemeint, dass sich die Prepared Statements automatisch darum "kümmern", dass keine Injektion möglich ist.

    htmlspecialchars werde ich natürlich verwenden, um XSS zu verhindern.

    Sollte ich denn password_hash und password_verify nutzen? Habe mir extra so eine schöne Funktion gebaut, die einen zufälligen Salt aus irgendwelchen Symbolen erstellt und daraus einen Hash (SHA512) generiert. Und das mit 10.000 Iterationen. Ist das nicht sicher genug?
    Habe mir extra so eine schöne Funktion gebaut, die einen zufälligen Salt aus irgendwelchen Symbolen erstellt und daraus einen Hash (SHA512) generiert. Und das mit 10.000 Iterationen. Ist das nicht sicher genug?


    Ja... und eventuell nein:
    Die SHA-Algorithmen sind auf Effizienz ausgelegt und daher prinzipiell für Passwörter ungeeignet. Algorithmen wie PBKDF2 und bcrypt sind dagegen speziell für Passwörter entwickelt.
    Aus naiver Sicht würde ich mich dazu durchringen, dass man das mit genügt Iteration vermutlich nachstellen kann.
    Auf der anderen Seite bin ich kein Experte auf dem Thema und vertraue lieber auf etwas, was von Leuten mit mehr Ahnung von der Thematik entwickelt, getestet und ggf. auch mathematisch bewiesen wurde und allgemein als sicher gilt.
    Auch wenn das mit SHA512, Salt und ein paar Iteration trivial umzusetzen ist, die Gefahr das man selbst dabei irgend einen anscheinend unbedeutenden Fehler macht, ist größer als die Wahrscheinlichkeit, dass die API von PHP einen solchen Fehler aufweist.

    Das ganze ist vermutlich auch etwas "Geschmackssache". Sicherheit ist natürlich (in der IT) extrem wichtig und bei gewissen Sachen ist es absolute Pflicht sich darum zu kümmern (z.B. SQL-Injections verhindern). Wie man sich darum kümmert liegt letztlich bei jedem selbst. Ich versuche mich an best practises zu halten, die vom Kern her auch solide sind. Die konsequente Verwendung von PDO::quote ala @Link und der richtige Einsatz von SHA512+Salt+Iterationen ist für 99,999% der Fälle vermutlich genau so sicher wie die hier bennanten Alternativen. Aber warum nicht noch ein kleines bisschen Sicherheit mehr, wenn es keine Nachteile mit sich bringt?

    Und speziell auf Password-Hashing: Was wäre der Nachteil, wenn man die PHP-Funktionen nutzt? Aus meiner Sicht gibt es keinen, die allgemeine Empfehlung lautet die API zu nutzen und es ist nachvollziehbar: also nutze und empfehle ich die API.
    Danke für die Antwort.

    Dann werde ich demnächst wohl die Funktion herausnehmen und sie durch password_hash und password_verify ersetzen. Sicher ist meine Funktion trotzdem, auch wenn die API natürlich sicherer und besser sein wird.

    /EDIT:
    Was ist damit?

    GCAsk schrieb:

    Ich meine gelesen zu haben, man soll die emulierten Prepared Statements unbedingt ausschalten.