Wie erstelle ich eine sichere vorbereitete MySQL-Anweisung in PHP?

Lesezeit: 7 Minuten

Ich bin neu in der Verwendung vorbereiteter Anweisungen in MySQL mit PHP. Ich brauche Hilfe beim Erstellen einer vorbereiteten Anweisung zum Abrufen von Spalten.

Ich brauche Informationen aus verschiedenen Spalten. Derzeit für eine Testdatei verwende ich die völlig unsicher SQL-Anweisung:

$qry = "SELECT * FROM mytable where userid='{$_GET['userid']}' AND category='{$_GET['category']}'ORDER BY id DESC"
$result = mysql_query($qry) or die(mysql_error()); 

Kann mir jemand helfen eine zu erstellen sicher mysql-Anweisung unter Verwendung von Eingaben aus URL-Parametern (wie oben), die vorbereitet sind?

BONUS: Vorbereitete Anweisungen sollen auch die Geschwindigkeit erhöhen. Erhöht es die Gesamtgeschwindigkeit, wenn ich eine vorbereitete Anweisung nur drei- oder viermal auf einer Seite verwende?

  • mögliches Duplikat von Best way to stop SQL Injection in PHP

    – aussen

    26. März 2012 um 2:05 Uhr

Wie erstelle ich eine sichere vorbereitete MySQL Anweisung in PHP
Bernstein

Hier ist ein Beispiel mit mysqli (Objektsyntax – ziemlich einfach in Funktionssyntax zu übersetzen, wenn Sie möchten):

$db = new mysqli("host","user","pw","database");
$stmt = $db->prepare("SELECT * FROM mytable where userid=? AND category=? ORDER BY id DESC");
$stmt->bind_param('ii', intval($_GET['userid']), intval($_GET['category']));
$stmt->execute();

$stmt->store_result();
$stmt->bind_result($column1, $column2, $column3);

while($stmt->fetch())
{
    echo "col1=$column1, col2=$column2, col3=$column3 \n";
}

$stmt->close();

Wenn Sie außerdem assoziative Arrays (zur Verwendung mit SELECT *) auf einfache Weise abrufen möchten, anstatt genau angeben zu müssen, an welche Variablen gebunden werden soll, finden Sie hier eine praktische Funktion:

function stmt_bind_assoc (&$stmt, &$out) {
    $data = mysqli_stmt_result_metadata($stmt);
    $fields = array();
    $out = array();

    $fields[0] = $stmt;
    $count = 1;

    while($field = mysqli_fetch_field($data)) {
        $fields[$count] = &$out[$field->name];
        $count++;
    }
    call_user_func_array(mysqli_stmt_bind_result, $fields);
}

Um es zu verwenden, rufen Sie es einfach auf, anstatt bind_result aufzurufen:

$stmt->store_result();

$resultrow = array();
stmt_bind_assoc($stmt, $resultrow);

while($stmt->fetch())
{
    print_r($resultrow);
}

  • Danke. Wie zeige ich die Ergebnisse dieser Anweisung an? Im Allgemeinen verwende ich mysql_fetch_row, funktioniert das hier?

    – Chris

    17. August 2009 um 23:20 Uhr

  • Aktualisiert, um ein Beispiel hinzuzufügen, wie man die Ergebnisse erhält.

    – Bernstein

    17. August 2009 um 23:26 Uhr

  • Beachten Sie auch, dass in meinem Beispiel davon ausgegangen wird, dass beide Parameter Ganzzahlen sind. Wenn es sich um Zeichenfolgen handelt, müssen Sie die Argumente für bind_param entsprechend ändern.

    – Bernstein

    17. August 2009 um 23:32 Uhr

Wie erstelle ich eine sichere vorbereitete MySQL Anweisung in PHP
Vinko Vrsalović

Sie können stattdessen Folgendes schreiben:

$qry = "SELECT * FROM mytable where userid='";
$qry.= mysql_real_escape_string($_GET['userid'])."' AND category='";
$qry.= mysql_real_escape_string($_GET['category'])."' ORDER BY id DESC";

Aber um vorbereitete Anweisungen zu verwenden, verwenden Sie besser eine generische Bibliothek, wie z PDO

<?php
/* Execute a prepared statement by passing an array of values */
$sth = $dbh->prepare('SELECT * FROM mytable where userid=? and category=? 
                      order by id DESC');
$sth->execute(array($_GET['userid'],$_GET['category']));
//Consider a while and $sth->fetch() to fetch rows one by one
$allRows = $sth->fetchAll(); 
?>

Oder mit mysql

<?php
$link = mysqli_connect("localhost", "my_user", "my_password", "world");

/* check connection */
if (mysqli_connect_errno()) {
    printf("Connect failed: %s\n", mysqli_connect_error());
    exit();
}

$category = $_GET['category'];
$userid = $_GET['userid'];

/* create a prepared statement */
if ($stmt = mysqli_prepare($link, 'SELECT col1, col2 FROM mytable where 
                      userid=? and category=? order by id DESC')) {
    /* bind parameters for markers */
    /* Assumes userid is integer and category is string */
    mysqli_stmt_bind_param($stmt, "is", $userid, $category);  
    /* execute query */
    mysqli_stmt_execute($stmt);
    /* bind result variables */
    mysqli_stmt_bind_result($stmt, $col1, $col2);
    /* fetch value */
    mysqli_stmt_fetch($stmt);
    /* Alternative, use a while:
    while (mysqli_stmt_fetch($stmt)) {
        // use $col1 and $col2 
    }
    */
    /* use $col1 and $col2 */
    echo "COL1: $col1 COL2: $col2\n";
    /* close statement */
    mysqli_stmt_close($stmt);
}

/* close connection */
mysqli_close($link);
?>

Ich stimme mehreren anderen Antworten zu:

  • PHPs ext/mysql hat keine Unterstützung für parametrisierte SQL-Anweisungen.
  • Abfrageparameter gelten als zuverlässiger beim Schutz vor SQL-Injection-Problemen.
  • mysql_real_escape_string() kann auch effektiv sein, wenn Sie es richtig verwenden, aber es ist ausführlicher zu codieren.
  • In einigen Versionen enthalten internationale Zeichensätze Fälle von Zeichen, die nicht ordnungsgemäß maskiert werden, wodurch subtile Schwachstellen entstehen. Die Verwendung von Abfrageparametern vermeidet diese Fälle.

Beachten Sie auch, dass Sie auch bei der Verwendung von Abfrageparametern immer noch vorsichtig mit der SQL-Injektion sein müssen, da Parameter in SQL-Abfragen nur die Stelle von Literalwerten einnehmen. Wenn Sie SQL-Abfragen dynamisch erstellen und PHP-Variablen für den Tabellennamen, Spaltennamen oder einen anderen Teil der SQL-Syntax verwenden, werden weder Abfrageparameter noch mysql_real_escape_string() Hilfe in diesem Fall. Zum Beispiel:

$query = "SELECT * FROM $the_table ORDER BY $some_column"; 

Bezüglich Leistung:

  • Der Leistungsvorteil entsteht, wenn Sie eine vorbereitete Abfrage mehrmals mit unterschiedlichen Parameterwerten ausführen. Sie vermeiden den Aufwand für das Analysieren und Vorbereiten der Abfrage. Aber wie oft müssen Sie dieselbe SQL-Abfrage viele Male in derselben PHP-Anforderung ausführen?
  • Selbst wenn Sie diesen Leistungsvorteil nutzen können, ist dies normalerweise nur eine geringfügige Verbesserung im Vergleich zu vielen anderen Dingen, die Sie tun könnten, um die Leistung zu verbessern, wie z. B. die effektive Verwendung von Opcode-Caching oder Daten-Caching.
  • Es gibt sogar einige Fälle, in denen eine vorbereitete Abfrage verwendet wird schadet Leistung. Im folgenden Fall kann der Optimierer beispielsweise nicht davon ausgehen, dass er einen Index für die Suche verwenden kann, da er den Parameterwert annehmen muss könnte Beginnen Sie mit einem Platzhalter:

    SELECT * FROM mytable WHERE textfield LIKE ?
    

  • ausgezeichnete Antwort. Wie würden Sie vorschlagen, dass ich mit einer Anweisung wie “SELECT * FROM mytable WHERE textfield LIKE” umgehe? Soll ich eine vorbereitete Anweisung oder etwas anderes verwenden?

    – Chris

    18. August 2009 um 22:00 Uhr

  • In diesem speziellen Fall müssen Sie das Muster in die Abfragezeichenfolge interpolieren: “SELECT * FROM mytable WHERE textfield LIKE 'word%'“. Auf diese Weise kann der Optimierer feststellen, dass er einen Index verwenden kann (wenn Ihr Muster natürlich mit einem Platzhalter beginnt, bietet ein Index sowieso keinen Vorteil).

    – Bill Karwin

    18. August 2009 um 22:15 Uhr

Sicherheit mit MySQL in PHP (oder jeder anderen Sprache für diese Angelegenheit) ist ein viel diskutiertes Thema. Hier sind ein paar Orte, an denen Sie sich tolle Tipps holen können:

Die beiden wichtigsten Punkte sind meiner Meinung nach:

  • SQL-Injektion: Achten Sie darauf, alle Ihre Abfragevariablen mit PHP zu maskieren mysql_real_escape_string() Funktion (oder so ähnlich).
  • Eingabevalidierung: Vertrauen Sie niemals der Eingabe des Benutzers. Sehen Das für ein Tutorial, wie Sie Ihre Eingaben richtig bereinigen und validieren.

Wenn Sie mysqli verwenden – was mir die beste Lösung zu sein scheint – empfehle ich dringend, eine Kopie von herunterzuladen codesense_mysqli Klasse.

Es ist eine hübsche kleine Klasse, die den größten Teil des Krams zusammenfasst und verbirgt, der sich bei der Verwendung von rohem mysqli ansammelt, sodass die Verwendung vorbereiteter Anweisungen nur ein oder zwei zusätzliche Zeilen über der alten mysql/php-Schnittstelle erfordert

  • Diese Klasse ist extrem einfach zu verwenden. Ich frage mich, warum so etwas nicht beliebter ist.

    – Chris

    18. August 2009 um 17:31 Uhr

1646933047 702 Wie erstelle ich eine sichere vorbereitete MySQL Anweisung in PHP
Ader

Ziemlich spät, aber das könnte jemandem helfen:

/**
* Execute query method. Executes a user defined query
*
* @param string $query the query statement
* @param array(Indexed) $col_vars the column variables to replace parameters. The count value should equal the number of supplied parameters
*
* Note: Use parameters in the query then supply the respective replacement variables in the second method parameter. e.g. 'SELECT COUNT(*) as count FROM foo WHERE bar = ?'
*
* @return array
*/
function read_sql($query, $col_vars=null) {
    $conn = new mysqli('hostname', 'username', 'user_pass', 'dbname');
    $types = $variables = array();
    if (isset($col_vars)) {
        for ($x=0; $x<count($col_vars); $x++) {
            switch (gettype($col_vars[$x])) {
                case 'integer':
                    array_push($types, 'i');
                        break;
                case 'double':
                    array_push($types, 'd');
                    break;
                default:
                    array_push($types, 's');
            }
            array_push($variables, $col_vars[$x]);
        }
        $types = implode('', $types);
        $sql = $conn->prepare($query);
        $sql -> bind_param($types, ...$variables);
        $sql -> execute();
        $results = $sql -> get_result();
        $sql -> close();
    }else {
        $results = $conn->query($query) or die('Error: '.$conn->error);
    }
    if ($results -> num_rows > 0) {
        while ($row = $results->fetch_assoc()) {
            $result[] = $row;
        }
        return $result;
    }else {
        return null;
    }
}

Sie können die Funktion dann wie folgt aufrufen:

read_sql('SELECT * FROM mytable where userid = ? AND category = ? ORDER BY id DESC', array($_GET['userid'], $_GET['category']));

  • Diese Klasse ist extrem einfach zu bedienen. Ich frage mich, warum so etwas nicht beliebter ist.

    – Chris

    18. August 2009 um 17:31 Uhr

988560cookie-checkWie erstelle ich eine sichere vorbereitete MySQL-Anweisung in PHP?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy