mysql how - Come posso prevenire l'SQL injection in PHP?




to make (24)

Ogni risposta qui copre solo una parte del problema. In effetti, ci sono quattro diverse parti di query che possiamo aggiungere dinamicamente:

  • una stringa
  • un numero
  • un identificatore
  • una parola chiave di sintassi.

E le dichiarazioni preparate riguardano solo due di esse.

Ma a volte dobbiamo rendere la nostra query ancora più dinamica, aggiungendo anche operatori o identificatori. Quindi, avremo bisogno di diverse tecniche di protezione.

In generale, tale approccio di protezione si basa sulla whitelist .

In questo caso, ogni parametro dinamico dovrebbe essere hardcoded nel tuo script e scelto da quel set. Ad esempio, per fare l'ordinamento dinamico:

$orders  = array("name", "price", "qty"); // Field names
$key     = array_search($_GET['sort'], $orders)); // See if we have such a name
$orderby = $orders[$key]; // If not, first one will be set automatically. smart enuf :)
$query   = "SELECT * FROM `table` ORDER BY $orderby"; // Value is safe

Tuttavia, esiste un altro modo per proteggere gli identificatori: l'escape. Finché hai un identificatore quotato, puoi sfuggire gli apici inversamente raddoppiandoli.

Come ulteriore passo, possiamo prendere in prestito un'idea davvero geniale dell'uso di un segnaposto (un proxy per rappresentare il valore effettivo nella query) dalle dichiarazioni preparate e inventare un segnaposto di un altro tipo - un segnaposto identificatore.

Quindi, per farla breve: è un segnaposto , non una dichiarazione preparata può essere considerata una pallottola d'argento.

Quindi, una raccomandazione generale può essere formulata come Fintanto che si aggiungono parti dinamiche alla query utilizzando segnaposti (e questi segnaposto elaborati correttamente, naturalmente), si può essere sicuri che la query è sicura .

Tuttavia, c'è un problema con le parole chiave di sintassi SQL (come AND , DESC e simili), ma l'elenco in bianco sembra l'unico approccio in questo caso.

Aggiornare

Sebbene vi sia un accordo generale sulle migliori pratiche relative alla protezione dell'iniezione SQL, ci sono ancora molte cattive pratiche. E alcuni di loro sono troppo radicati nelle menti degli utenti di PHP. Ad esempio, proprio in questa pagina ci sono (anche se invisibili alla maggior parte dei visitatori) più di 80 risposte cancellate - tutte rimosse dalla comunità a causa della cattiva qualità o della promozione di pratiche cattive e obsolete. Peggio ancora, alcune delle cattive risposte non vengono cancellate, ma piuttosto prosperose.

Ad esempio, there(1) are(2) still(3) many(4) answers(5) , inclusa la share suggerisce l'escaping manuale delle stringhe - un approccio obsoleto che si è dimostrato insicuro.

Oppure c'è una risposta leggermente migliore che suggerisce solo un altro metodo per la formattazione delle stringhe e addirittura la vanta come la panacea definitiva. Mentre, naturalmente, non lo è. Questo metodo non è migliore della normale formattazione delle stringhe, ma conserva tutti i suoi svantaggi: è applicabile solo alle stringhe e, come qualsiasi altra formattazione manuale, è essenzialmente facoltativa, non obbligatoria, soggetta a errori umani di qualsiasi tipo.

Penso che tutto questo a causa di una superstizione molto antica, supportata da autorità come OWASP o il manuale PHP , che proclama l'uguaglianza tra qualsiasi "fuga" e protezione dalle iniezioni SQL.

Indipendentemente da ciò che il manuale PHP ha detto per anni, *_escape_string non rende assolutamente sicuri i dati e non è mai stato previsto. Oltre ad essere inutile per qualsiasi parte SQL diversa dalla stringa, l'escape manuale è errato, perché è manuale come opposto all'automazione.

E OWASP lo rende ancora peggiore, sottolineando la necessità di sfuggire all'input dell'utente, il che è un'assurdità assoluta: non dovrebbero esserci parole del genere nel contesto della protezione dell'iniezione. Ogni variabile è potenzialmente pericolosa, indipendentemente dalla fonte! O, in altre parole, ogni variabile deve essere formattata correttamente per essere inserita in una query, indipendentemente dalla fonte. È la destinazione che conta. Nel momento in cui uno sviluppatore inizia a separare le pecore dalle capre (pensando se qualche variabile particolare è "sicura" o meno) lui / lei fa il suo primo passo verso il disastro. Per non parlare del fatto che anche la formulazione suggerisce la fuga di massa al punto di ingresso, simile alla funzione di virgolette stesse - già disprezzata, deprecata e rimossa.

Quindi, a differenza di qualsiasi "fuga", le istruzioni preparate sono la misura che protegge dall'iniezione SQL (quando applicabile).

Se non sei ancora convinto, ecco una spiegazione passo-passo che ho scritto, La guida di Hitchhiker's Guide to SQL Injection , in cui ho spiegato tutti questi argomenti in dettaglio e ho anche compilato una sezione interamente dedicata alle cattive pratiche e alla loro divulgazione.

Se l'input dell'utente viene inserito senza modifiche in una query SQL, l'applicazione diventa vulnerabile all'iniezione SQL , come nell'esempio seguente:

$unsafe_variable = $_POST['user_input']; 

mysql_query("INSERT INTO `table` (`column`) VALUES ('$unsafe_variable')");

Questo perché l'utente può inserire qualcosa come value'); DROP TABLE table;-- value'); DROP TABLE table;-- , e la query diventa:

INSERT INTO `table` (`column`) VALUES('value'); DROP TABLE table;--')

Cosa si può fare per evitare che ciò accada?


Una buona idea è usare un 'mappatore di oggetti relazionale' come Idiorm :

$user = ORM::for_table('user')
->where_equal('username', 'j4mie')
->find_one();

$user->first_name = 'Jamie';
$user->save();

$tweets = ORM::for_table('tweet')
    ->select('tweet.*')
    ->join('user', array(
        'user.id', '=', 'tweet.user_id'
    ))
    ->where_equal('user.username', 'j4mie')
    ->find_many();

foreach ($tweets as $tweet) {
    echo $tweet->text;
}

Non ti salva solo da iniezioni SQL ma anche da errori di sintassi! Supporta anche raccolte di modelli con concatenamento di metodi per filtrare o applicare azioni a più risultati contemporaneamente e connessioni multiple.


Esistono molti modi per prevenire le iniezioni SQL e altri hack SQL. Puoi trovarlo facilmente su Internet (Ricerca Google). Ovviamente DOP è una delle buone soluzioni. Ma vorrei suggerirvi una buona prevenzione dei link da SQL Injection.

Che cos'è l'iniezione SQL e come prevenire

Manuale PHP per l'iniezione SQL

Spiegazione Microsoft di SQL injection e prevenzione in PHP

e altri come Preventing SQL injection con MySQL e PHP

Ora, perché è necessario impedire la query dall'iniezione SQL?

Vorrei farti sapere: Perché proviamo a prevenire l'SQL injection con un breve esempio qui sotto:

Query per la corrispondenza di autenticazione di accesso:

$query="select * from users where email='".$_POST['email']."' and password='".$_POST['password']."' ";

Ora, se qualcuno (un hacker) mette

$_POST['email']= [email protected]' OR '1=1

e password nulla ....

La query verrà analizzata nel sistema solo fino a:

$query="select * from users where email='[email protected]' OR '1=1';

L'altra parte verrà scartata. Quindi, cosa accadrà? Un utente non autorizzato (hacker) sarà in grado di accedere come amministratore senza avere la sua password. Ora, può fare tutto ciò che l'amministratore / persona email può fare. Vedi, è molto pericoloso se l'iniezione SQL non viene prevenuta.


Uso tre diversi modi per impedire che la mia applicazione Web sia vulnerabile all'iniezione SQL.

  1. L'uso di mysql_real_escape_string(), che è una funzione predefinita in PHP , e questo codice aggiungono le barre rovesciate per i seguenti caratteri: \x00, \n, \r, \, ', "e \x1a. Passa i valori di input come parametri per ridurre al minimo la possibilità di SQL injection.
  2. Il modo più avanzato è utilizzare i PDO.

Spero che questo ti possa aiutare.

Considera la seguente query:

$iId = mysql_real_escape_string("1 OR 1=1"); $sSql = "SELECT * FROM table WHERE id = $iId";

mysql_real_escape_string () non proteggerà qui. Se usi le virgolette singole ('') attorno alle tue variabili all'interno della tua query è ciò che ti protegge da questo. Ecco una soluzione qui sotto per questo:

$iId = (int) mysql_real_escape_string("1 OR 1=1"); $sSql = "SELECT * FROM table WHERE id = $iId";

Questa question ha alcune buone risposte a riguardo.

Suggerisco, usando PDO è l'opzione migliore.

Modificare:

mysql_real_escape_string()è deprecato da PHP 5.5.0. Usa mysqli o PDO.

È un'alternativa a mysql_real_escape_string ()

string mysqli_real_escape_string ( mysqli $link , string $escapestr )

Esempio:

$iId = $mysqli->real_escape_string("1 OR 1=1");
$mysqli->query("SELECT * FROM table WHERE id = $iId");

Come puoi vedere, le persone suggeriscono di utilizzare al massimo dichiarazioni preparate. Non è sbagliato, ma quando la tua query viene eseguita solo una volta per processo, ci sarà una leggera penalizzazione delle prestazioni.

Stavo affrontando questo problema, ma penso di averlo risolto in modo molto sofisticato - il modo in cui gli hacker usano per evitare di usare le virgolette. L'ho usato in congiunzione con dichiarazioni preparate emulate. Lo uso per prevenire tutti i tipi di possibili attacchi SQL injection.

Il mio approccio:

  • Se ti aspetti che l'input sia intero, assicurati che sia veramente intero. In un linguaggio di tipo variabile come PHP è molto importante. Ad esempio, è possibile utilizzare questa soluzione molto semplice ma potente: sprintf("SELECT 1,2,3 FROM table WHERE 4 = %u", $input);

  • Se ti aspetti altro dall'esempio intero lo esagero . Se lo esagoni, sfuggirai perfettamente a tutti gli input. In C / C ++ c'è una funzione chiamata mysql_hex_string() , in PHP puoi usare bin2hex() .

    Non preoccuparti che la stringa con escape avrà una dimensione 2x della sua lunghezza originale perché anche se usi mysql_real_escape_string , PHP deve allocare la stessa capacità ((2*input_length)+1) , che è lo stesso.

  • Questo metodo esadecimale viene spesso utilizzato quando si trasferiscono dati binari, ma non vedo alcun motivo per non utilizzarlo su tutti i dati per evitare attacchi di SQL injection. Nota che devi anteporre i dati con 0x o usare invece la funzione MySQL UNHEX .

Quindi, ad esempio, la query:

SELECT password FROM users WHERE name = 'root'

Diventerà:

SELECT password FROM users WHERE name = 0x726f6f74

o

SELECT password FROM users WHERE name = UNHEX('726f6f74')

Hex è la fuga perfetta. Nessun modo di iniettare.

Differenza tra la funzione UNHEX e il prefisso 0x

C'è stata una discussione nei commenti, quindi alla fine voglio chiarire. Questi due approcci sono molto simili, ma in qualche modo sono leggermente diversi:

Il prefisso ** 0x ** può essere utilizzato solo per colonne di dati come char, varchar, text, block, binary, ecc .
Inoltre, il suo uso è un po 'complicato se stai per inserire una stringa vuota. Dovrai sostituirlo interamente con '' o avrai un errore.

UNHEX () funziona su qualsiasi colonna; non devi preoccuparti della stringa vuota.

I metodi esadecimali sono spesso usati come attacchi

Si noti che questo metodo esadecimale viene spesso utilizzato come attacco di iniezione SQL in cui gli interi sono come le stringhe e sono fuggiti solo con mysql_real_escape_string . Quindi puoi evitare l'uso di virgolette.

Ad esempio, se fai solo qualcosa del genere:

"SELECT title FROM article WHERE id = " . mysql_real_escape_string($_GET["id"])

un attacco può iniettarti molto facilmente . Considera il seguente codice iniettato restituito dallo script:

SELEZIONA ... WHERE id = -1 union seleziona select table_name da information_schema.tables

e ora basta estrarre la struttura della tabella:

SELEZIONA ... WHERE id = -1 unione seleziona select nome_colonna da information_schema.column dove table_name = 0x61727469636c65

E poi seleziona solo i dati che vuoi. Non è bello?

Ma se il programmatore di un sito iniettabile lo esageri, nessuna iniezione sarebbe possibile perché la query sarebbe simile a questa: SELECT ... WHERE id = UNHEX('2d312075...3635')


Ho scritto questa piccola funzione diversi anni fa:

function sqlvprintf($query, $args)
{
    global $DB_LINK;
    $ctr = 0;
    ensureConnection(); // Connect to database if not connected already.
    $values = array();
    foreach ($args as $value)
    {
        if (is_string($value))
        {
            $value = "'" . mysqli_real_escape_string($DB_LINK, $value) . "'";
        }
        else if (is_null($value))
        {
            $value = 'NULL';
        }
        else if (!is_int($value) && !is_float($value))
        {
            die('Only numeric, string, array and NULL arguments allowed in a query. Argument '.($ctr+1).' is not a basic type, it\'s type is '. gettype($value). '.');
        }
        $values[] = $value;
        $ctr++;
    }
    $query = preg_replace_callback(
        '/{(\\d+)}/', 
        function($match) use ($values)
        {
            if (isset($values[$match[1]]))
            {
                return $values[$match[1]];
            }
            else
            {
                return $match[0];
            }
        },
        $query
    );
    return $query;
}

function runEscapedQuery($preparedQuery /*, ...*/)
{
    $params = array_slice(func_get_args(), 1);
    $results = runQuery(sqlvprintf($preparedQuery, $params)); // Run query and fetch results.   
    return $results;
}

Ciò consente di eseguire istruzioni in una stringa C # -ish one-liner.Formato come:

runEscapedQuery("INSERT INTO Whatever (id, foo, bar) VALUES ({0}, {1}, {2})", $numericVar, $stringVar1, $stringVar2);

Fugge considerando il tipo di variabile. Se provate a parametrizzare la tabella, i nomi delle colonne, fallirebbero perché mette ogni stringa tra virgolette che è una sintassi non valida.

AGGIORNAMENTO DELLA SICUREZZA: la str_replaceversione precedente consentiva le iniezioni aggiungendo {#} token ai dati dell'utente. Questa preg_replace_callbackversione non causa problemi se la sostituzione contiene questi token.


Alcune linee guida per l'escape di caratteri speciali nelle istruzioni SQL.

Non usare MySQL , questa estensione è deprecata, usa MySQLi o PDO .

MySQLi

Per eseguire manualmente l'escape di caratteri speciali in una stringa, puoi utilizzare la funzione mysqli_real_escape_string . La funzione non funzionerà correttamente a meno che il set di caratteri corretto sia impostato con mysqli_set_charset .

Esempio:

$mysqli = new mysqli( 'host', 'user', 'password', 'database' );
$mysqli->set_charset( 'charset');

$string = $mysqli->real_escape_string( $string );
$mysqli->query( "INSERT INTO table (column) VALUES ('$string')" );

Per l'escape automatico dei valori con istruzioni preparate, utilizzare mysqli_prepare e mysqli_stmt_bind_param dove devono essere forniti i tipi per le variabili di collegamento corrispondenti per una conversione appropriata:

Esempio:

$stmt = $mysqli->prepare( "INSERT INTO table ( column1, column2 ) VALUES (?,?)" );

$stmt->bind_param( "is", $integer, $string );

$stmt->execute();

Non importa se usi istruzioni preparate o mysqli_real_escape_string, devi sempre conoscere il tipo di dati di input con cui stai lavorando.

Pertanto, se si utilizza un'istruzione preparata, è necessario specificare i tipi di variabili per la funzione mysqli_stmt_bind_param.

E l'uso di mysqli_real_escape_string è per, come dice il nome, l'escape di caratteri speciali in una stringa, quindi non renderà gli interi sicuri. Lo scopo di questa funzione è di impedire la rottura delle stringhe nelle istruzioni SQL e il danno al database che potrebbe causare. mysqli_real_escape_string è una funzione utile se usata correttamente, specialmente se combinata con sprintf.

Esempio:

$string = "x' OR name LIKE '%John%";
$integer = '5 OR id != 0';

$query = sprintf( "SELECT id, email, pass, name FROM members WHERE email ='%s' AND id = %d", $mysqli->real_escape_string( $string ), $integer );

echo $query;
// SELECT id, email, pass, name FROM members WHERE email ='x\' OR name LIKE \'%John%' AND id = 5

$integer = '99999999999999999999';
$query = sprintf( "SELECT id, email, pass, name FROM members WHERE email ='%s' AND id = %d", $mysqli->real_escape_string( $string ), $integer );

echo $query;
// SELECT id, email, pass, name FROM members WHERE email ='x\' OR name LIKE \'%John%' AND id = 2147483647

Qualunque cosa tu faccia finisca di usare, assicurati di controllare che il tuo input non sia già stato storpiato da magic_quotes o da qualche altra spazzatura ben intenzionata, e se necessario, stripslashes attraverso stripslashes o qualsiasi altra cosa per sanitizzarlo.


Un modo semplice sarebbe quello di utilizzare un framework PHP come CodeIgniter o Laravel che hanno funzioni integrate come il filtro e la registrazione attiva in modo da non doversi preoccupare di queste sfumature.


** Attenzione: l'approccio descritto in questa risposta si applica solo a scenari molto specifici e non è sicuro poiché gli attacchi di SQL injection non si basano solo sulla possibilità di iniettare X=Y. **

Se gli hacker tentano di hackerare il modulo tramite la $_GETvariabile di PHP o con la stringa di query dell'URL, sarai in grado di catturarli se non sono sicuri.

RewriteCond %{QUERY_STRING} ([0-9]+)=([0-9]+)
RewriteRule ^(.*) ^/track.php

Perché 1=1, 2=2, 1=2, 2=1, 1+1=2, ecc ... sono le domande comuni a un database SQL di un attaccante. Forse è anche usato da molte applicazioni di hacking.

Ma devi stare attento, che non devi riscrivere una query sicura dal tuo sito. Il codice sopra ti dà un consiglio, per riscrivere o reindirizzare (dipende da te) quella stringa di query dinamica specifica per l'hacking in una pagina che memorizzerà l' indirizzo IP dell'utente malintenzionato , ANCORA I LORO COOKIES, la cronologia, il browser o qualsiasi altro elemento sensibile informazioni, in modo da poterle affrontare in seguito mettendo al bando il loro account o contattando le autorità.


Preferisco le stored procedure ( MySQL ha avuto il supporto delle stored procedure da 5.0 ) da un punto di vista della sicurezza - i vantaggi sono -

  1. La maggior parte dei database (incluso MySQL ) consente di limitare l'accesso degli utenti all'esecuzione di stored procedure. Il controllo dell'accesso di sicurezza a grana fine è utile per prevenire l'escalation degli attacchi con privilegi. Ciò impedisce alle applicazioni compromesse di essere in grado di eseguire SQL direttamente sul database.
  2. Essi astraggono la query SQL raw dall'applicazione in modo da rendere disponibili all'applicazione meno informazioni sulla struttura del database. Ciò rende più difficile per le persone comprendere la struttura sottostante del database e progettare attacchi appropriati.
  3. Accettano solo parametri, quindi i vantaggi delle query parametrizzate sono lì. Ovviamente, IMO è ancora necessario disinfettare il tuo input, specialmente se stai utilizzando SQL dinamico all'interno della stored procedure.

Gli svantaggi sono -

  1. Loro (procedure memorizzate) sono difficili da mantenere e tendono a moltiplicarsi molto rapidamente. Questo rende la gestione di un problema.
  2. Non sono molto adatti per le query dinamiche - se sono costruiti per accettare il codice dinamico come parametri, molti dei vantaggi vengono annullati.

Consiglio di utilizzare PDO (PHP Data Objects) per eseguire query SQL con parametri.

Non solo protegge da SQL injection, ma velocizza anche le query.

E usando le mysql_ PDO piuttosto che mysql_ , mysqli_ e pgsql_ , si rende l'app un po 'più astratta dal database, nella rara eventualità che si debba cambiare fornitore di database.


Ci sono così tante risposte per PHP e MySQL , ma qui c'è il codice per PHP e Oracle per prevenire l'SQL injection così come l'uso regolare dei driver oci8:

$conn = oci_connect($username, $password, $connection_string);
$stmt = oci_parse($conn, 'UPDATE table SET field = :xx WHERE ID = 123');
oci_bind_by_name($stmt, ':xx', $fieldval);
oci_execute($stmt);

Penso che se qualcuno vuole usare PHP e MySQL o qualche altro server dataBase:

  1. Pensa all'apprendimento di PDO (PHP Data Objects): si tratta di un livello di accesso al database che fornisce un metodo uniforme di accesso a più database.
  2. Pensa all'apprendimento di MySQLi
  3. Usa le funzioni native di PHP come: strip_tags , mysql_real_escape_string() o se variabile numerica, solo (int)$foo. Leggi di più sul tipo di variabili in PHP here . Se stai usando librerie come PDO o MySQLi, usa sempre PDO::quote() e mysqli_real_escape_string() .

Esempi di librerie:

---- DOP

----- Nessun segnaposto - maturo per l'iniezione SQL! È cattivo

$request = $pdoConnection->("INSERT INTO parents (name, addr, city) values ($name, $addr, $city)");

----- Segnaposto senza nome

$request = $pdoConnection->("INSERT INTO parents (name, addr, city) values (?, ?, ?);

----- Segnaposto con nome

$request = $pdoConnection->("INSERT INTO parents (name, addr, city) value (:name, :addr, :city)");

--- MySQLi

$request = $mysqliConnection->prepare('
       SELECT * FROM trainers
       WHERE name = ?
       AND email = ?
       AND last_login > ?');

    $query->bind_param('first_param', 'second_param', $mail, time() - 3600);
    $query->execute();

PS :

DOP vince questa battaglia con facilità. Con il supporto di dodici diversi driver di database e parametri con nome, possiamo ignorare la piccola perdita di prestazioni e abituarci alla sua API. Dal punto di vista della sicurezza, entrambi sono sicuri fino a quando lo sviluppatore li usa come dovrebbero essere usati

Ma mentre sia PDO che MySQLi sono piuttosto veloci, MySQLi ha prestazioni insignificanti più veloci nei benchmark: ~ 2,5% per le dichiarazioni non preparate e ~ 6,5% per quelle preparate.

E per favore prova ogni query nel tuo database - è un modo migliore per prevenire l'iniezione.



Avviso di sicurezza : questa risposta non è in linea con le migliori pratiche di sicurezza. L'escaping è inadeguato per prevenire l'iniezione SQL , utilizzare invece istruzioni preparate . Utilizzare la strategia descritta di seguito a proprio rischio. (Inoltre, mysql_real_escape_string() stato rimosso in PHP 7.)

Potresti fare qualcosa di base come questo:

$safe_variable = mysql_real_escape_string($_POST["user-input"]);
mysql_query("INSERT INTO table (column) VALUES ('" . $safe_variable . "')");

Questo non risolverà tutti i problemi, ma è un ottimo trampolino di lancio. Ho omesso elementi ovvi come controllare l'esistenza della variabile, il formato (numeri, lettere, ecc.).


IMPORTANTE

Il modo migliore per prevenire SQL Injection è utilizzare le istruzioni preparate anziché l'escaping , come dimostra la risposta accettata .

Esistono librerie come Aura.Sql e EasyDB che consentono agli sviluppatori di utilizzare le istruzioni preparate più facilmente. Per saperne di più sul motivo per cui le istruzioni preparate sono migliori nell'arresto di SQL injection , fare riferimento a questo bypass mysql_real_escape_string() e alle vulnerabilità SQL Injection di SQL Unicode recentemente risolte in WordPress .

Prevenzione mysql_real_escape_string() - mysql_real_escape_string()

PHP ha una funzione appositamente creata per prevenire questi attacchi. Tutto quello che devi fare è usare la bocca piena di una funzione, mysql_real_escape_string .

mysql_real_escape_string prende una stringa che verrà utilizzata in una query MySQL e restituirà la stessa stringa con tutti i tentativi di iniezione SQL in modo sicuro. Fondamentalmente, sostituirà quelle citazioni fastidiose (') che un utente potrebbe inserire con un sostituto sicuro di MySQL, una citazione di escape \'.

NOTA: per utilizzare questa funzione è necessario essere connessi al database!

// Connetti a MySQL

$name_bad = "' OR 1'"; 

$name_bad = mysql_real_escape_string($name_bad);

$query_bad = "SELECT * FROM customers WHERE username = '$name_bad'";
echo "Escaped Bad Injection: <br />" . $query_bad . "<br />";


$name_evil = "'; DELETE FROM customers WHERE 1 or username = '"; 

$name_evil = mysql_real_escape_string($name_evil);

$query_evil = "SELECT * FROM customers WHERE username = '$name_evil'";
echo "Escaped Evil Injection: <br />" . $query_evil;

Puoi trovare maggiori dettagli in MySQL - SQL Injection Prevention .


Per chi non è sicuro di come usare PDO (proveniente dalle mysql_funzioni), ho realizzato un wrapper PDO molto, molto semplice, che è un singolo file. Esiste per mostrare quanto sia facile fare tutte le cose comuni che le applicazioni devono essere fatte. Funziona con PostgreSQL, MySQL e SQLite.

In sostanza, leggerlo PDO per vedere come mettere le funzioni DOP da utilizzare nella vita reale per rendere più semplice per archiviare e recuperare i valori nel formato che si desidera.

Voglio una singola colonna

$count = DB::column('SELECT COUNT(*) FROM `user`);

Voglio un risultato dell'array (chiave => valore) (es. Per creare una selectbox)

$pairs = DB::pairs('SELECT `id`, `username` FROM `user`);

Voglio un risultato a riga singola

$user = DB::row('SELECT * FROM `user` WHERE `id` = ?', array($user_id));

Voglio una serie di risultati

$banned_users = DB::fetch('SELECT * FROM `user` WHERE `banned` = ?', array(TRUE));

La semplice alternativa a questo problema potrebbe essere risolta concedendo autorizzazioni appropriate nel database stesso. Ad esempio: se si utilizza un database mysql, inserire il database tramite terminale o l'interfaccia utente fornita e seguire semplicemente questo comando:

 GRANT SELECT, INSERT, DELETE ON database TO [email protected]'localhost' IDENTIFIED BY 'password';

Ciò limiterà l'utente a essere limitato solo con la query specificata. Rimuovi l'autorizzazione di eliminazione e quindi i dati non verranno mai eliminati dalla query generata dalla pagina php. La seconda cosa da fare è svuotare i privilegi in modo che mysql aggiorni le autorizzazioni e gli aggiornamenti.

FLUSH PRIVILEGES; 

ulteriori informazioni su flush .

Per vedere i privilegi attuali per l'utente, attiva la seguente query.

select * from mysql.user where User='username';

Ulteriori informazioni su GRANT .


Usare PDO e MYSQLi è una buona pratica per prevenire le iniezioni SQL, ma se si vuole veramente lavorare con le funzioni e le query MySQL, sarebbe meglio usare

mysql_real_escape_string()

$unsafe_variable = mysql_real_escape_string($_POST['user_input']);

Ci sono altre abilità per prevenire questo: come identificare - se l'input è una stringa, un numero, un carattere o un array, ci sono così tante funzioni in-built per rilevarlo. Inoltre, sarebbe meglio usare queste funzioni per verificare i dati di input.

is_string

$unsafe_variable = (is_string($_POST['user_input']) ? $_POST['user_input'] : '');

is_numeric

$unsafe_variable = (is_numeric($_POST['user_input']) ? $_POST['user_input'] : '');

Ed è molto meglio usare queste funzioni per controllare i dati di input con mysql_real_escape_string.


Secondo me, il modo migliore per prevenire in genere l'iniezione SQL nella tua applicazione PHP (o qualsiasi applicazione web, peraltro) è pensare all'architettura della tua applicazione. Se l'unico modo per proteggersi dall'iniezione SQL è di ricordare di usare un metodo o una funzione speciale che fa The Right Thing ogni volta che parli al database, stai sbagliando. In questo modo, è solo una questione di tempo finché non ti dimentichi di formattare correttamente la query ad un certo punto del tuo codice.

Adottare il pattern MVC e un framework come CakePHP o CodeIgniter è probabilmente la strada giusta da percorrere: attività comuni come la creazione di query di database sicure sono state risolte e implementate centralmente in tali framework. Ti aiutano a organizzare la tua applicazione web in modo sensato e ti fanno pensare di più sul caricamento e sul salvataggio degli oggetti piuttosto che sulla costruzione sicura di singole query SQL.


Usa PDO e query preparate.

( $conn è un oggetto PDO )

$stmt = $conn->prepare("INSERT INTO tbl VALUES(:id, :name)");
$stmt->bindValue(':id', $id);
$stmt->bindValue(':name', $name);
$stmt->execute();

Per quanto riguarda molte risposte utili, spero di aggiungere alcuni valori a questo thread. SQL injection è un attacco che può essere eseguito tramite input dell'utente (input compilati dall'utente e quindi utilizzati all'interno di query), i pattern di iniezione SQL sono sintassi di query corretta mentre possiamo chiamarli: query non valide per motivi non validi, si assume che potrebbe essere una persona cattiva che cerca di ottenere informazioni segrete (bypassando il controllo degli accessi) che influenzano i tre principi di sicurezza (Riservatezza, integrità, disponibilità).

Ora, il nostro scopo è prevenire le minacce alla sicurezza come gli attacchi SQL injection, la domanda (come prevenire l'attacco SQL injection usando PHP), essere più realistici, filtrare i dati o cancellare i dati di input è il caso quando si usano dati di input dell'utente all'interno di tali query, utilizzando PHP o qualsiasi altro linguaggio di programmazione non è il caso, o come raccomandato da più persone a utilizzare la tecnologia moderna come la dichiarazione preparata o altri strumenti che attualmente supportano la prevenzione dell'iniezione SQL, ritengono che questi strumenti non siano più disponibili? Come proteggi la tua applicazione?

Il mio approccio contro l'SQL injection è: cancellare i dati di input dell'utente prima di inviarli al database (prima di utilizzarlo in qualsiasi query).

Filtraggio dei dati per (Conversione di dati non sicuri in dati sicuri) Considera che PDO e MySQLi non sono disponibili, come puoi proteggere la tua applicazione? Mi costringi a usarli? Che dire di altre lingue oltre a PHP? Preferisco fornire idee generali in quanto possono essere utilizzate per un confine più ampio non solo per una lingua specifica.

  1. Utente SQL (limitando il privilegio dell'utente): le operazioni SQL più comuni sono (SELEZIONA, AGGIORNA, INSERISCA), quindi, perché fornire il privilegio UPDATE a un utente che non lo richiede? Ad esempio , l'accesso e le pagine di ricerca utilizzano solo SELECT, quindi perché utilizzare gli utenti DB in queste pagine con privilegi elevati? REGOLA: non creare un utente del database per tutti i privilegi, per tutte le operazioni SQL, è possibile creare il proprio schema come (deluser, selectuser, updateuser) come nomi utente per un facile utilizzo.

vedi il principio del privilegio minimo

  1. Filtraggio dei dati: prima di creare qualsiasi query, l'input dell'utente deve essere convalidato e filtrato, per i programmatori, è importante definire alcune proprietà per ogni variabile di input dell'utente: tipo di dati, modello di dati e lunghezza dei dati . un campo che è un numero compreso tra (x e y) deve essere esattamente convalidato usando la regola esatta, per un campo che è una stringa (testo): il modello è il caso, per esempio, il nome utente deve contenere solo alcuni caratteri per dire [a- zA-Z0-9_-.] la lunghezza varia tra (x e n) dove x e n (interi, x <= n). Regola: creare filtri esatti e regole di convalida sono le migliori pratiche per me.

  2. Usa altri strumenti: qui, inoltre, sono d'accordo con te che la dichiarazione preparata (interrogazione parametrica) e le procedure memorizzate, gli svantaggi qui sono questi modi richiede competenze avanzate che non esistono per la maggior parte degli utenti, l'idea di base qui è quella di distinguere tra query SQL e i dati che vengono utilizzati all'interno, entrambi gli approcci possono essere utilizzati anche con dati non sicuri, poiché qui i dati di input dell'utente non aggiungono nulla alla query originale come (any o x = x). Per ulteriori informazioni, leggere il foglio Cheat di prevenzione dell'iniezione SQL di OWASP .

Ora, se sei un utente avanzato, inizia a usare questa difesa come preferisci, ma, per i principianti, se non sono in grado di implementare rapidamente la stored procedure e preparato la dichiarazione, è meglio filtrare i dati di input il più possibile.

Infine, consideriamo che l'utente invia questo testo qui sotto invece di inserire il suo nome utente:

[1] UNION SELECT IF(SUBSTRING(Password,1,1)='2',BENCHMARK(100000,SHA1(1)),0) User,Password FROM mysql.user WHERE User = 'root'

Questo input può essere controllato in anticipo senza alcuna istruzione preparata e stored procedure, ma per essere al sicuro, il loro utilizzo inizia dopo il filtraggio e la convalida dei dati dell'utente.

L'ultimo punto è la rilevazione di comportamenti imprevisti che richiedono maggiore sforzo e complessità; non è raccomandato per le normali applicazioni web. Il comportamento imprevisto nell'input dell'utente sopra è SELECT, UNION, IF, SUBSTRING, BENCHMARK, SHA, root una volta rilevate queste parole, è possibile evitare l'input.

Update1:

Un utente ha commentato che questo post è inutile, OK! Ecco cosa OWASP.ORG fornito OWASP.ORG :

Difese primarie:

opzione n. 1: uso di istruzioni preparate (query parametrizzate)
Opzione n. 2: uso delle stored procedure
Opzione n. 3: sfuggire a tutti gli input forniti dall'utente

Difese aggiuntive:

anche imposizione: privilegio minimo
Esegui anche: convalida dell'inoltro lista bianca

Come forse saprai, la rivendicazione di un articolo dovrebbe essere supportata da argomenti validi, almeno un riferimento! Altrimenti, è considerato un attacco e una cattiva affermazione!

Update2:

Dal manuale PHP, PHP: istruzioni preparate - Manuale :

Escaping e SQL injection

Le variabili associate verranno automaticamente sfuggite dal server. Il server inserisce i propri valori di escape nelle posizioni appropriate nel modello dell'istruzione prima dell'esecuzione. È necessario fornire un suggerimento al server per il tipo di variabile associata, per creare una conversione appropriata. Vedere la funzione mysqli_stmt_bind_param () per maggiori informazioni.

L'escaping automatico dei valori all'interno del server è talvolta considerato una funzione di sicurezza per prevenire l'iniezione SQL. Lo stesso grado di sicurezza può essere raggiunto con le istruzioni non preparate se i valori di input sono sfuggiti correttamente.

Update3:

Ho creato casi di test per sapere come PDO e MySQLi inviano la query al server MySQL quando si utilizza la dichiarazione preparata:

DOP:

$user = "''1''"; //Malicious keyword
$sql = 'SELECT * FROM awa_user WHERE userame =:username';
$sth = $dbh->prepare($sql, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
$sth->execute(array(':username' => $user));

Log delle query:

    189 Query SELECT * FROM awa_user WHERE userame ='\'\'1\'\''
    189 Quit

MySQLi:

$stmt = $mysqli->prepare("SELECT * FROM awa_user WHERE username =?")) {
$stmt->bind_param("s", $user);
$user = "''1''";
$stmt->execute();

Log delle query:

    188 Prepare   SELECT * FROM awa_user WHERE username =?
    188 Execute   SELECT * FROM awa_user WHERE username ='\'\'1\'\''
    188 Quit

È chiaro che una dichiarazione preparata sfugge anche ai dati, nient'altro.

Come accennato anche nella precedente dichiarazione The automatic escaping of values within the server is sometimes considered a security feature to prevent SQL injection. The same degree of security can be achieved with non-prepared statements, if input values are escaped correctly, ciò dimostra che la convalida dei dati intval()è una buona idea per i valori interi prima di inviare qualsiasi query, inoltre, prevenire i dati dannosi prima di inviare la query è un approccio corretto e valido .

Si prega di vedere questa domanda per ulteriori dettagli: PDO invia query raw a MySQL mentre Mysqli invia query preparate, entrambi producono lo stesso risultato

Riferimenti:

  1. SQL Cheat Sheet di Injection
  2. SQL Injection
  3. Informazioni di sicurezza
  4. Principi di sicurezza
  5. Convalida dei dati

Trovo molto lunghe le risposte di cui sopra, quindi riassumiamo:

L'estensione mysqli ha una serie di vantaggi, i miglioramenti chiave sull'estensione mysql sono:

  • Interfaccia orientata agli oggetti
  • Supporto per le dichiarazioni preparate
  • Supporto per più dichiarazioni
  • Supporto per le transazioni
  • Funzionalità di debug migliorate
  • Supporto del server integrato

Fonte: panoramica MySQLi

Come spiegato nelle risposte precedenti, le alternative a mysql sono mysqli e PDO (PHP Data Objects).

  • L'API supporta le dichiarazioni preparate sul lato server: supportate da MYSQLi e PDO
  • L'API supporta le istruzioni preparate lato client: supportate solo da PDO
  • API supporta stored procedure: sia MySQLi che PDO
  • API supporta più dichiarazioni e tutte le funzionalità di MySQL 4.1+ - Supportato da MySQLi e principalmente anche da PDO

Sia MySQLi che PDO sono stati introdotti in PHP 5.0, mentre MySQL è stato introdotto prima di PHP 3.0. Un punto da notare è che MySQL è incluso in PHP5.x anche se deprecato nelle versioni successive.





php mysql sql security sql-injection