Warum kein "sicheres" Standard-PHP für Datenbank-Abfrage ohne SQL-Injection

Kerchner

Cadet 1st Year
Registriert
Apr. 2020
Beiträge
12
Kann mir bitte mal jemand erklären warum es seitens der PHP-Entwickler, die immer wieder neue Versionen rausbringen, nicht möglich ist eine PHP-Version zu entwickeln bei der man keine extra "Befehle" (so ennne ich dies mal) angeben muss damit z.B. eine Datenbank-Afrage nicht per SQL-Injection "infiziert" werden kann?

Mir ist bewusst, dass das Problem wohl nicht allein an PHP liegt, sondern auch an der eingesetzten Datenbank.
Da man aber die Daten meist mittels PHP abfragt, müsste sich hier auch PHP den Schuh anziehen.
Auch klar, es gibt .ASP etc. ... also nicht nur PHP.... egal an dieser Stelle!
Ich kenn mich da auch nicht aus, also bitte hakt nicht darauf rum.;)

Mir geht es - als Nicht-Programmierer - darum, dass man um eine sichere Datenbank-Abfrage realisieren zu können z.B. prepared-Statements oder "veraltete Escapes" nutzen muss. Diese muss man aber expliziet angeben!
PHP:
mysql_real_escape_string
    
mysqli::real_escape_string
mysqli::escape_string
mysqli_real_escape_string
    
$mysqli->prepare

PDO::prepare
Warum kann man sowas nicht "automatisch" (im Hintergrund) mit einer normalen Abfrage "verknüpfen" indem man quasi sagt, dass man das nicht händisch tippen muss, es also Standard ist?!
Quasi, dass jede Abfrage ohne Angabe von prepared-Statements oder "veraltete / Escapes" dennoch "sicher" sind ohne SQLi zuzulassen.
Das wäre doch für alle Seiten viel einfacher!

Möchte keine Diskussion entfachen... sondern eben nur wissen warum das nicht möglich ist.
 
Das ist im Allgemeinen nicht möglich weil der Code nicht weiß, was da unsicher ist.

Vielleicht willst Du ja eine komplexere Abfrage aus mehreren Zeichenketten zusammen bauen. Wenn das dann escaped wird, funktioniert die Abfrage so nicht.
Daher ist das mit dem prepare schon sinnvoll. Dann ist klar, was die echten Parameter sein sollen, die keinen Scheiß enthalten dürfen.
 
Ich bin auch ein klein wenig gespannt. Ich würde spontan Rückwärts-Kompatibilität vermuten? Heutige Frameworks und Libs (also generell, nicht unbedingt PHP) sind jedenfalls eher so gebaut, dass der einfachste Weg schon sicher ist.

Haggis schrieb:
Das ist im Allgemeinen nicht möglich weil der Code nicht weiß, was da unsicher ist.
Man könnte es ja durchaus so bauen, dass alles per Default escaped wird und man es explizit ausschalten muss. Oder raw-sql generell erschweren.
 
Die standard Treiber myslq und mysqli sind halt ziemlich low level.
Wenn du PDO benutzt, kannst du schon variblen escapen. Besser noch man nutzt einfach eine Library, zB doctrine orm.

PHP kann die variablen nicht automatisch escapen, da es durchaus viele Anwendungsfälle gibt, wo der SQL query string konkateniert wird oder es beabsichtigt ist, dass bestimmt SQL Konstrukte rauskommen.

Solche Low Level Treiber und somit SQL injections sind auch in anderen Programmiersprachen möglich.
 
Das unsichere ist ja nicht das nicht verwenden der Prepared Statements sondern das Zusammensetzen des SQL Commands über Stringoperationen ohne dabei (korrektes) Escaping zu benutzen. Wenn man so einen SQL Command String an ein Prepared Statement übergibt ist das genauso unsicher!

Und nein, es gibt keine automatische Logik die einem das Escaping nachträglich durchführen kann, da nicht mehr unterscheidbar ist was nun gewollt ist und was nicht.
 
Jesterfox schrieb:
Wenn man so einen SQL Command String an ein Prepared Statement übergibt ist das genauso unsicher!
Wirklich?
Man liest i.d.R. anderes.
 
Ob ich den String "SELECT * FROM USERS WHERE NAME='%1';" wo ich das %1 durch "USER';DROP DATABASE;" ersetzt hab nun über herkömmlichen Weg oder als Stored Procedure ausführe ist für das Ergebnis egal ;-)

Stored Procedures sind nur dann sicher wenn man die Parameter für das Statement auch wirklich als solche übergibt, da dann die Logik dahinter das Escaping für einen übernimmt (oder es erst gar nicht notwendig ist, je nach dem wie die Kommunikation mit der Datenbank dann aussieht)
 
Jesterfox schrieb:
Stored Procedures sind nur dann sicher wenn man die Parameter für das Statement auch wirklich als solche übergibt, da dann die Logik dahinter das Escaping für einen übernimmt (oder es erst gar nicht notwendig ist,
Genau das ist dsoch wie Stored Procedures funktionieren, wenn man einen Parameter übergibt.
 
Ja, klar. Ich wollt damit nur andeuten wieso da kein Automatismus möglich ist der für einen das Denken übernimmt. Man muss das mit der Parameterübergabe händisch ausprogrammieren, weil nur so klar ist was ein Parameter ist der entsprechend behandelt werden muss.
 
Ich hatte mir irgendwann eine Funktion geschrieben, die den String und ein Array mit den Parametern erwartet. Anschließend benutzt diese Funktion ein global verfügbares PDO. Anschließend hat die Funktion den Rest gemacht.

PHP:
function databaseAccess($query, $params = NULL, $output = FALSE) {
    global $db;
    $query = explode(";", $query);
    if (count($query) == 2 AND empty($query[1]) OR count($query) == 1) {
    $query = $query[0];
    }
    if (is_array($query)) {
    $i = 0;
    foreach ($query AS &$entry) {
        $c = substr_count($entry, "?") + $i;
        $parameter = array();
        for (; $i < $c; $i++) {
        array_push($parameter, $params[$i]);
        }
        databaseAccess($entry, $parameter, $output);
    }
    return;
    }
    if (empty($query)) {
    return;
    }
    $stmt = $db->prepare($query);
    if (substr_count($query, '?') > 0 AND substr_count($query, '?') != count($params)) {
    throw new Exception('Platzhalteranzahl stimmt nicht mit der Parameteranzahl überein!');
    }
    if (is_array($params)) {
    for ($i = 0; $i < sizeof($params); ++$i) {
        //debug_print_backtrace();
        $stmt->bindParam($i + 1, $params[$i]);
    }
    }
    $stmt->execute();
    if ($output === TRUE) {
    if ($params != null) {
        foreach ($params AS $param) {
        $query = substr_replace($query, (is_numeric($param) ? '' : '"') . $param . (is_numeric($param) ? '' : '"'), strpos($query, '?'), 1);
        }
    }
    echo "<pre>" . SQLColor($query) . "</pre><hr />";
    }
    if ($stmt->errorInfo()[0] != "00000") {
    if (!empty($_SESSION[Configuration::system]['user'])) {
        global $adminemail;
        $adminemail->setSubject("SQL-Statementfehler");
        $adminemail->setMessage("Fehler in der Abfrage.<br />\nFehlercode: " . $stmt->errorInfo()[1] . "<br />\nFehlermeldung: " . $stmt->errorInfo()[2] . "<br />\nSQL-Statement: $query");
        $adminemail->sendMail();
        if (Configuration::debug === true) {
        echo debug_print_backtrace();
        }
    } else {
        echo"Fehler in der Abfrage.<br />\nFehlercode: " . $stmt->errorInfo()[1] . "<br />\nFehlermeldung: " . $stmt->errorInfo()[2] . "<br />\nSQL-Statement: $query";
        if (Configuration::debug === true) {
        echo debug_print_backtrace();
        }
    }
    die("Fehler in der Datenbankabfrage.<br />Der Administrator wurde per Mail benachrichtigt");
    } else {
    return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }
}

Mittlerweile würde ich das nicht mehr so machen, weil wenn ich mir das so durchlese kann ich mir nur an die Stirn fassen. Heute würde ich dafür einen OO Ansatz wählen.
 
Jesterfox schrieb:
Ja, klar. Ich wollt damit nur andeuten wieso da kein Automatismus möglich ist der für einen das Denken übernimmt. Man muss das mit der Parameterübergabe händisch ausprogrammieren, weil nur so klar ist was ein Parameter ist der entsprechend behandelt werden muss.
In Frameworks nimmt man für 95% seiner Queries sowieso eine andere API her. Beispiel Laravel:
Code:
$user = DB::table('users')->where('name', 'John')->first();
Solche Frameworks wiederum sollte man sowieso verwenden, außer zum lernen von PHP vllt.
 
BeBur schrieb:
. Oder raw-sql generell erschweren.
Für den ganzen 0815-Mist ok. Aber wenn du mal bissel Perf-Optimierung betreiben musst, sind diese ganzen Wrapper totaler Schrott. Oder mal etwas komplexere Queries bauen musst.
 
  • Gefällt mir
Reaktionen: KitKat::new()
GroMag schrieb:
Für den ganzen 0815-Mist ok. Aber wenn du mal bissel Perf-Optimierung betreiben musst, sind diese ganzen Wrapper totaler Schrott. Oder mal etwas komplexere Queries bauen musst.
Schon klar. Ich schrieb ja auch:
BeBur schrieb:
In Frameworks nimmt man für 95% seiner Queries sowieso eine andere API her. Beispiel Laravel:
Die restlichen 5% schreibt man raw. Von mir aus auch deutlich mehr wenn man viel optimieren muss. Aber das ganze 08/15 und 99% was man als Anfänger schreibt ist damit zumindest abgedeckt. Es geht hier im Thread ja um die Frage von sinnigen d.h. sicheren Defaults.
 
GroMag schrieb:
Aber wenn du mal bissel Perf-Optimierung betreiben musst, sind diese ganzen Wrapper totaler Schrott. Oder mal etwas komplexere Queries bauen musst.
Wobei ich das dann als stored Procedure in die DB legen würde und dann auch wieder über den Wrapper drauf zugreifen würde. Das macht dann die Schnittstelle zur DB einheitlich.
 
  • Gefällt mir
Reaktionen: BeBur
Zurück
Oben