prevention - sql php security




Come posso prevenire l'SQL injection in PHP? (19)

Attenzione: il codice di esempio di questa risposta (come il codice di esempio della domanda) utilizza l'estensione mysql di PHP, che è stata deprecata in PHP 5.5.0 e completamente rimossa in PHP 7.0.0.

Se stai usando una versione recente di PHP, l'opzione mysql_real_escape_string descritta di seguito non sarà più disponibile (sebbene mysqli::escape_string sia un equivalente moderno). In questi giorni l'opzione mysql_real_escape_string avrebbe senso solo per il codice legacy su una vecchia versione di PHP.

Hai due opzioni: sfuggire i caratteri speciali nella unsafe_variable o utilizzare una query con parametri. Entrambi ti proteggeranno dall'iniezione SQL. La query parametrizzata è considerata la migliore pratica, ma prima di poter essere utilizzata è necessario passare a una nuova estensione mysql in PHP.

Tratteremo prima la stringa di impatto inferiore che sfugge a una.

//Connect

$unsafe_variable = $_POST["user-input"];
$safe_variable = mysql_real_escape_string($unsafe_variable);

mysql_query("INSERT INTO table (column) VALUES ('" . $safe_variable . "')");

//Disconnect

Vedi anche, i dettagli della funzione mysql_real_escape_string .

Per utilizzare la query parametrizzata, è necessario utilizzare MySQLi piuttosto che le funzioni MySQL . Per riscrivere il tuo esempio, avremmo bisogno di qualcosa come il seguente.

<?php
    $mysqli = new mysqli("server", "username", "password", "database_name");

    // TODO - Check that connection was successful.

    $unsafe_variable = $_POST["user-input"];

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

    // TODO check that $stmt creation succeeded

    // "s" means the database expects a string
    $stmt->bind_param("s", $unsafe_variable);

    $stmt->execute();

    $stmt->close();

    $mysqli->close();
?>

La funzione chiave su cui vorresti leggere sarebbe mysqli::prepare .

Inoltre, come altri hanno suggerito, potresti trovare utile / più facile aumentare uno strato di astrazione con qualcosa come PDO .

Tieni presente che il caso che hai chiesto è abbastanza semplice e che casi più complessi potrebbero richiedere approcci più complessi. In particolare:

  • Se si desidera modificare la struttura di SQL in base all'input dell'utente, le query con parametri non saranno di aiuto e l'escape richiesto non è coperto da mysql_real_escape_string . In questo tipo di casi, sarebbe meglio passare l'input dell'utente attraverso una whitelist per garantire che solo i valori "sicuri" siano consentiti.
  • Se si utilizzano numeri interi da input dell'utente in una condizione e si utilizza l'approccio mysql_real_escape_string , si verificherà il problema descritto da Polynomial nei commenti seguenti. Questo caso è più complicato perché gli interi non sono racchiusi tra virgolette, quindi è possibile gestirli convalidando che l'input dell'utente contenga solo cifre.
  • Ci sono probabilmente altri casi di cui non sono a conoscenza. Potresti scoprire che this è una risorsa utile su alcuni dei problemi più sottili che puoi incontrare.

https://code.i-harness.com

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?


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.).


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')


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.


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.


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.


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();

Utilizzare istruzioni preparate e query parametrizzate. Queste sono istruzioni SQL che vengono inviate e analizzate dal server database separatamente da qualsiasi parametro. In questo modo è impossibile per un utente malintenzionato iniettare SQL dannoso.

Fondamentalmente hai due opzioni per ottenere questo:

  1. Utilizzo di PDO (per qualsiasi driver di database supportato):

    $stmt = $pdo->prepare('SELECT * FROM employees WHERE name = :name');
    
    $stmt->execute(array('name' => $name));
    
    foreach ($stmt as $row) {
        // do something with $row
    }
    
  2. Usando MySQLi (per MySQL):

    $stmt = $dbConnection->prepare('SELECT * FROM employees WHERE name = ?');
    $stmt->bind_param('s', $name); // 's' specifies the variable type => 'string'
    
    $stmt->execute();
    
    $result = $stmt->get_result();
    while ($row = $result->fetch_assoc()) {
        // do something with $row
    }
    

Se ci si connette a un database diverso da MySQL, esiste una seconda opzione specifica del driver a cui è possibile fare riferimento (ad esempio pg_prepare() e pg_execute() per PostgreSQL). DOP è l'opzione universale.

Configurare correttamente la connessione

Si noti che quando si utilizza PDO per accedere a un database MySQL le istruzioni preparate non vengono utilizzate per impostazione predefinita . Per risolvere questo problema è necessario disabilitare l'emulazione delle istruzioni preparate. Un esempio di creazione di una connessione tramite PDO è:

$dbConnection = new PDO('mysql:dbname=dbtest;host=127.0.0.1;charset=utf8', 'user', 'pass');

$dbConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$dbConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

Nell'esempio precedente la modalità errore non è strettamente necessaria, ma si consiglia di aggiungerla . In questo modo lo script non si fermerà con un Fatal Error quando qualcosa va storto. E dà allo sviluppatore la possibilità di catch eventuali errori che vengono PDOException come PDOException .

Ciò che è obbligatorio , tuttavia, è la prima riga setAttribute() , che indica a PDO di disabilitare le istruzioni preparate emulate e utilizzare istruzioni preparate in modo reale . Ciò assicura che l'istruzione e i valori non vengano analizzati da PHP prima di inviarli al server MySQL (dando a un possibile utente malintenzionato alcuna possibilità di iniettare SQL malizioso).

Sebbene sia possibile impostare il charset nelle opzioni del costruttore, è importante notare che le versioni "precedenti" di PHP (<5.3.6) ignoravano in silenzio il parametro charset nel DSN.

Spiegazione

Quello che succede è che l'istruzione SQL che passi per essere prepare viene analizzata e compilata dal server del database. Specificando i parametri (un parametro ? O un parametro denominato come :name nell'esempio precedente) si indica al motore del database dove si desidera filtrare. Quindi quando si chiama execute , l'istruzione preparata viene combinata con i valori dei parametri specificati.

La cosa importante qui è che i valori dei parametri sono combinati con l'istruzione compilata, non con una stringa SQL. L'iniezione SQL funziona ingannando lo script includendo stringhe dannose quando crea SQL da inviare al database. Quindi, inviando l'SQL effettivo separatamente dai parametri, si limita il rischio di finire con qualcosa che non si intendeva. Tutti i parametri che invii quando usi una dichiarazione preparata saranno trattati come stringhe (sebbene il motore di database possa fare un po 'di ottimizzazione in modo che i parametri possano finire anche come numeri, ovviamente). Nell'esempio sopra, se la variabile $name contiene 'Sarah'; DELETE FROM employees 'Sarah'; DELETE FROM employees il risultato sarebbe semplicemente una ricerca per la stringa "'Sarah'; DELETE FROM employees" , e non si finirà con una tabella vuota .

Un altro vantaggio dell'utilizzo di istruzioni preparate è che se si esegue la stessa istruzione più volte nella stessa sessione, verrà analizzata e compilata solo una volta, ottenendo alcuni guadagni di velocità.

Oh, e dal momento che hai chiesto come fare per un inserto, ecco un esempio (usando PDO):

$preparedStatement = $db->prepare('INSERT INTO table (column) VALUES (:column)');

$preparedStatement->execute(array('column' => $unsafeValue));

È possibile utilizzare istruzioni preparate per query dinamiche?

Mentre è ancora possibile utilizzare istruzioni preparate per i parametri di query, la struttura della query dinamica non può essere parametrizzata e alcune funzionalità di query non possono essere parametrizzate.

Per questi scenari specifici, la cosa migliore da fare è utilizzare un filtro di lista bianca che limiti i valori possibili.

// Value whitelist
// $dir can only be 'DESC' otherwise it will be 'ASC'
if (empty($dir) || $dir !== 'DESC') {
   $dir = 'ASC';
}

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.


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.


** 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à.


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);

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 .


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));

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.

Se possibile, lancia i tipi dei tuoi parametri. Ma funziona solo su tipi semplici come int, bool e float.

$unsafe_variable = $_POST['user_id'];

$safe_variable = (int)$unsafe_variable ;

mysqli_query($conn, "INSERT INTO table (column) VALUES ('" . $safe_variable . "')");

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.


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.


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.







sql-injection