Mysql
 sql >> Database >  >> RDS >> Mysql

Passare alle dichiarazioni preparate

Sono stato nella stessa situazione. Stavo usando anche istruzioni concatenate, quindi ho cambiato la mia applicazione in istruzioni preparate.

le cattive notizie è che cambierai ogni istruzione SQL creata concatenando i dati del client all'istruzione SQL, che sarà quasi ogni istruzione SQL che hai nei tuoi 50 file di origine.

le buone notizie è il guadagno dal passaggio alle dichiarazioni preparate non ha prezzo, ad esempio:

1-non sarai mai preoccupato per qualcosa chiamato "attacco SQL Injection"

il manuale dice

Per me, quel motivo -la pace della mente- è sufficiente per pagare il costo della modifica del mio codice sorgente. , ora i tuoi clienti possono digitare un campo nome modulo robert; DROP table students; -- ;) e ti senti sicuro che non succederà nulla

2- non è più necessario sfuggire ai parametri del client. puoi usarli direttamente nell'istruzione SQL, qualcosa come :

$query = "SELECT FROM user WHERE id = ?";
$vars[] = $_POST['id'];

invece di

$id = $mysqli->real_escape_string($_POST['id']);
$query = "SELECT FROM user WHERE id = $id";

che è qualcosa che dovevi fare prima di usare affermazioni preparate, il che ti metteva in pericolo di dimenticare di sfuggire a un parametro come un normale essere umano. e tutto ciò che serve a un utente malintenzionato per corrompere il tuo sistema è solo 1 parametro senza caratteri di escape.

Cambiare il codice

in genere la modifica dei file di origine è sempre rischiosa e dolorosa, soprattutto se la progettazione del software è scadente e se non si dispone di un piano di test evidente. ma ti dirò cosa ho fatto per renderlo il più semplice possibile.

Ho creato una funzione che verrà utilizzata da ogni codice di interazione del database, quindi puoi modificare ciò che desideri in un secondo momento in un unico posto, quella funzione, puoi creare qualcosa del genere

class SystemModel
{
    /**
     * @param string $query
     * @param string $types
     * @param array $vars
     * @param \mysqli $conn
     * @return boolean|$stmt
     */
    public function preparedQuery($query,$types, array $vars, $conn)
    {
        if (count($vars) > 0) {
            $hasVars = true;
        }
        array_unshift($vars, $types);
        $stmt = $conn->prepare($query);
        if (! $stmt) {
            return false;
        }
        if (isset($hasVars)) {
            if (! call_user_func_array(array( $stmt, 'bind_param'), $this->refValues($vars))) {
                return false;
            }
        }
        $stmt->execute();
        return $stmt;
    }

    /* used only inside preparedQuery */
    /* code taken from: https://stackoverflow.com/a/13572647/5407848 */
    protected function refValues($arr)
    {
        if (strnatcmp(phpversion(), '5.3') >= 0) {
            $refs = array();
            foreach ($arr as $key => $value)
                $refs[$key] = &$arr[$key];
                return $refs;
        }
        return $arr;
    }
}

Ora puoi usare questa interfaccia ovunque tu voglia nei tuoi file sorgente, ad esempio cambiamo le tue attuali istruzioni SQL che hai fornito nella domanda. Cambiamo questo

$mysqli = new mysqli('localhost', "root", "", "testdb");
$addresult = "
                SELECT a.firstnames, a.surname, a.schoolrole, a.datejoined 
                FROM teachers a LEFT JOIN schools b ON a.schoolid = b.id 
                WHERE b.id = '".$inputvalues['schoolid']."'";

if( $result = $mysqli->query($addresult) ) {
    while($row = $result->fetch_all())
    {
        $returnResult = $row;
    }
}

In questo

$mysqli = new mysqli('localhost', "root", "", "testdb");
$sysModel = new SystemModel();
$addresult = "
                SELECT a.firstnames, a.surname, a.schoolrole, a.datejoined
                FROM teachers a LEFT JOIN schools b ON a.schoolid = b.id
                WHERE b.id = ?";
$types = "i"; // for more information on paramters types, please check :
//https://php.net/manual/en/mysqli-stmt.bind-param.php
$vars = [];
$vars[] = $inputvalues['schoolid'];

$stmt = $sysModel->preparedQuery($addresult, $types, $vars, $mysqli);
if (!$stmt || $stmt->errno) {
   die('error'); // TODO: change later for a better illustrative output
}
$result = $stmt->get_result();
$returnResult = [];
while ($row = $result->fetch_array(MYSQLI_ASSOC)) {
    $returnResult[] = $row;
}

Sì, l'attacco Sql Injection viene applicato concatenando una stringa non valida all'istruzione SQL. dove è un INSERT , SELECT , DELETE , UPDATE . per esempio

$query = "SELECT * FROM user WHERE name = '{$_GET['name']}' AND password = '{$_GET['pass']}'"

qualcosa del genere potrebbe essere sfruttato da

// exmaple.com?name=me&pass=1' OR 1=1; -- 

che risulterà in un'istruzione SQL

$query = "SELECT * FROM user WHERE name = 'me' AND password = '1' OR 1=1; -- '"
//executing the SQL statement and getting the result
if($result->num_rows){
    //user is authentic
}else{
    //wrong password
}
// that SQL will always get results from the table which will be considered a correct password

Buona fortuna con il passaggio del software alle istruzioni preparate e ricorda che la tranquillità che otterrai sapendo che qualunque cosa accada, sei al sicuro dagli attacchi di SQL injection vale il costo della modifica dei file di origine