Iniezione SQL che aggira mysql_real_escape_string ()


644

Esiste una possibilità di iniezione SQL anche durante l'utilizzo mysql_real_escape_string() funzione?

Considera questa situazione di esempio. SQL è costruito in PHP in questo modo:

$login = mysql_real_escape_string(GetFromPost('login'));
$password = mysql_real_escape_string(GetFromPost('password'));

$sql = "SELECT * FROM table WHERE login='$login' AND password='$password'";

Ho sentito numerose persone che mi dicono che un codice del genere è ancora pericoloso e possibile hackerare anche con mysql_real_escape_string() funzione utilizzata. Ma non riesco a pensare ad alcun possibile exploit?

Iniezioni classiche come questa:

aaa' OR 1=1 --

non lavorare.

Conosci qualche possibile iniezione che potrebbe passare attraverso il codice PHP sopra?


34
@ThiefMaster - Preferisco non dare errori dettagliati come utente non valido / password non valida ... dice ai commercianti di forza bruta che hanno un ID utente valido ed è solo la password che devono indovinare
Mark Baker

18
È orribile dal punto di vista dell'usabilità però. A volte non è possibile utilizzare il proprio nickname / nome utente / indirizzo e-mail principale e dimenticarlo dopo qualche tempo o il sito ha eliminato l'account per inattività. Quindi è estremamente fastidioso se continui a provare le password e potresti persino bloccare il tuo IP anche se è solo il tuo nome utente che non è valido.
ThiefMaster,

50
Per favore, non usare mysql_* funzioni nel nuovo codice . Non vengono più mantenuti e il processo di ammortamento è iniziato su di esso. Vedi la scatola rossa ? Scopriinvece le dichiarazioni preparate e usa PDO o MySQLi : questo articolo ti aiuterà a decidere quale. Se scegli DOP, ecco un buon tutorial .
Tereško,

13
@machineaddict, dal 5.5 (che è stato rilasciato di recente) le mysql_*funzioni producono già un E_DEPRECATEDavviso. L' ext/mysqlestensione non è stata mantenuta per più di 10 anni. Sei davvero così delirante?
tereško,

13
@machineaddict Hanno appena rimosso quell'estensione su PHP 7.0 e non è ancora del 2050.
GGG

Risposte:


379

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 ti proteggerà da questo. Il fatto che tu usi virgolette singole ( ' ') attorno alle tue variabili all'interno della tua query è ciò che ti protegge da questo. È inoltre disponibile un'opzione:

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

9
Ma questo non sarebbe un vero problema, perché mysql_query()non esegue più istruzioni, no?
Pekka,

11
@Pekka, anche se il solito esempio è DROP TABLE, in pratica l'attaccante ha maggiori probabilità di farlo SELECT passwd FROM users. In quest'ultimo caso, la seconda query viene di solito eseguita mediante una UNIONclausola.
Jacco,

58
(int)mysql_real_escape_string- questo non ha senso. Non differisce affatto da (int). E produrranno lo stesso risultato per ogni input
zerkms

28
Questo è più un uso improprio della funzione che altro. Dopotutto, è chiamato mysql_real_escape_string, non mysql_real_escape_integer. Non è indicato per essere utilizzato con campi interi.
NullUserException,

11
@ircmaxell, Eppure la risposta è totalmente fuorviante. Ovviamente la domanda è sul contenuto tra virgolette. "Le citazioni non ci sono" non è la risposta a questa domanda.
Pacerier,

629

La risposta breve è sì, sì c'è un modo per aggiraremysql_real_escape_string() .

Per casi molto spigolosi !!!

La lunga risposta non è così semplice. È basato su un attacco dimostrato qui .

L'attacco

Quindi, iniziamo mostrando l'attacco ...

mysql_query('SET NAMES gbk');
$var = mysql_real_escape_string("\xbf\x27 OR 1=1 /*");
mysql_query("SELECT * FROM test WHERE name = '$var' LIMIT 1");

In determinate circostanze, ciò restituirà più di 1 riga. Analizziamo cosa sta succedendo qui:

  1. Selezione di un set di caratteri

    mysql_query('SET NAMES gbk');

    Per questo tipo di attacco al lavoro, abbiamo bisogno della codifica che il server aspetta sulla connessione sia per codificare 'come in cioè ASCII 0x27 e di avere qualche personaggio il cui byte finale è un ASCII \es 0x5c. Come si è visto, ci sono 5 tali codifiche supportate in MySQL 5.6 di default: big5, cp932, gb2312, gbke sjis. Selezioneremo gbkqui.

    Ora, è molto importante notare l'uso di SET NAMESqui. Questo imposta il set di caratteri SUL SERVER . Se usassimo la chiamata alla funzione API C mysql_set_charset(), staremmo bene (sulle versioni di MySQL dal 2006). Ma di più sul perché in un minuto ...

  2. Il payload

    Il payload che useremo per questa iniezione inizia con la sequenza di byte 0xbf27. In gbk, questo è un carattere multibyte non valido; dentro latin1, è la stringa ¿'. Si noti che in latin1 e gbk , 0x27da solo, è letterale' personaggio .

    Abbiamo scelto questo payload perché, se lo chiamassimo addslashes(), inseriremmo un ASCII , \cioè 0x5cprima del 'personaggio. Così saremmo finire con 0xbf5c27, che a sua gbkè una sequenza di due caratteri: 0xbf5cseguito da 0x27. O in altre parole, un personaggio valido seguito da un non fuggito '. Ma non stiamo usando addslashes(). Quindi al passaggio successivo ...

  3. mysql_real_escape_string ()

    La chiamata dell'API C mysql_real_escape_string()differisce dal fatto addslashes()che conosce il set di caratteri di connessione. Quindi può eseguire correttamente l'escaping per il set di caratteri che il server si aspetta. Tuttavia, fino a questo punto, il client pensa che stiamo ancora usando latin1per la connessione, perché non l'abbiamo mai detto diversamente. Abbiamo detto al server che stiamo usando gbk, ma il client pensa ancora che lo sialatin1 .

    Quindi la chiamata a mysql_real_escape_string()inserire la barra rovesciata e abbiamo un 'carattere sospeso nel nostro contenuto "evaso"! In effetti, se dovessimo guardare $varnelgbk set di caratteri, vedremmo:

    縗 'O 1 = 1 / *

    È esattamente ciò che richiede l'attacco.

  4. The Query

    Questa parte è solo una formalità, ma ecco la query di rendering:

    SELECT * FROM test WHERE name = '縗' OR 1=1 /*' LIMIT 1

Congratulazioni, hai appena attaccato con successo un programma usando mysql_real_escape_string()...

Il cattivo

La situazione peggiora. PDOper impostazione predefinita emula le dichiarazioni preparate con MySQL. Ciò significa che sul lato client esegue sostanzialmente uno sprint mysql_real_escape_string()(nella libreria C), il che significa che quanto segue comporterà un'iniezione riuscita:

$pdo->query('SET NAMES gbk');
$stmt = $pdo->prepare('SELECT * FROM test WHERE name = ? LIMIT 1');
$stmt->execute(array("\xbf\x27 OR 1=1 /*"));

Ora, vale la pena notare che è possibile prevenirlo disabilitando le dichiarazioni preparate emulate:

$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

Questo di solito si tradurrà in una vera istruzione preparata (cioè i dati inviati in un pacchetto separato dalla query). Tuttavia, tieni presente che PDO riporterà silenziosamente all'emulazione di dichiarazioni che MySQL non può preparare in modo nativo: quelle che può essere elencata nel manuale, ma fai attenzione a selezionare la versione del server appropriata).

Il brutto

All'inizio ho detto che avremmo potuto prevenire tutto ciò se avessimo usato mysql_set_charset('gbk')invece di SET NAMES gbk. E questo è vero a condizione che tu stia usando una versione MySQL dal 2006.

Se stai usando una versione di MySQL in precedenza, poi un bug nel mysql_real_escape_string()fatto sì che i caratteri multibyte non validi, come quelli nel nostro payload sono stati trattati come singoli byte per scopi sfuggire anche se il cliente era stato correttamente informato della codifica di collegamento e così questo attacco sarebbe ci riesco ancora. Il bug è stato corretto in MySQL 4.1.20 , 5.0.22 e 5.1.11 .

Ma la parte peggiore è che PDOnon ha esposto l'API C mysql_set_charset()fino alla 5.3.6, quindi nelle versioni precedenti non può impedire questo attacco per ogni possibile comando! Ora è esposto come parametro DSN .

La grazia salvifica

Come abbiamo detto all'inizio, per far funzionare questo attacco, la connessione al database deve essere codificata usando un set di caratteri vulnerabili. nonutf8mb4 è vulnerabile e tuttavia può supportare tutti i caratteri Unicode: quindi puoi scegliere di usarlo invece - ma è disponibile solo da MySQL 5.5.3. Un'alternativa è utf8che non è anche vulnerabile e può supportare l'intero piano multilingue Unicode .

In alternativa, è possibile abilitare la NO_BACKSLASH_ESCAPESmodalità SQL, che (tra le altre cose) altera il funzionamento di mysql_real_escape_string(). Con questa modalità abilitata, 0x27verrà sostituita con 0x2727anziché anziché 0x5c27e quindi il processo di escape non può creare caratteri validi in nessuna delle codifiche vulnerabili in cui non esistevano in precedenza (ovvero 0xbf27è ancora 0xbf27ecc.) - quindi il server rifiuterà comunque la stringa come non valida . Tuttavia, vedi la risposta di @ eggyal per una diversa vulnerabilità che può derivare dall'utilizzo di questa modalità SQL.

Esempi sicuri

I seguenti esempi sono sicuri:

mysql_query('SET NAMES utf8');
$var = mysql_real_escape_string("\xbf\x27 OR 1=1 /*");
mysql_query("SELECT * FROM test WHERE name = '$var' LIMIT 1");

Perché il server si aspetta utf8...

mysql_set_charset('gbk');
$var = mysql_real_escape_string("\xbf\x27 OR 1=1 /*");
mysql_query("SELECT * FROM test WHERE name = '$var' LIMIT 1");

Perché abbiamo impostato correttamente il set di caratteri in modo che il client e il server corrispondano.

$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$pdo->query('SET NAMES gbk');
$stmt = $pdo->prepare('SELECT * FROM test WHERE name = ? LIMIT 1');
$stmt->execute(array("\xbf\x27 OR 1=1 /*"));

Perché abbiamo disattivato le dichiarazioni preparate emulate.

$pdo = new PDO('mysql:host=localhost;dbname=testdb;charset=gbk', $user, $password);
$stmt = $pdo->prepare('SELECT * FROM test WHERE name = ? LIMIT 1');
$stmt->execute(array("\xbf\x27 OR 1=1 /*"));

Perché abbiamo impostato correttamente il set di caratteri.

$mysqli->query('SET NAMES gbk');
$stmt = $mysqli->prepare('SELECT * FROM test WHERE name = ? LIMIT 1');
$param = "\xbf\x27 OR 1=1 /*";
$stmt->bind_param('s', $param);
$stmt->execute();

Perché MySQLi fa sempre delle vere dichiarazioni preparate.

Avvolgendo

Se tu:

  • Versioni uso moderno di MySQL (in ritardo 5.1, tutti 5.5, 5.6, ecc) E mysql_set_charset() / $mysqli->set_charset()parametro / del DSN PDO charset (in PHP ≥ 5.3.6)

O

  • Non utilizzare un set di caratteri vulnerabili per la codifica della connessione (usi solo utf8/ latin1/ ascii/ etc)

Sei sicuro al 100%.

Altrimenti, sei vulnerabile anche se stai usandomysql_real_escape_string() ...


3
L'emulazione DOP prepara dichiarazioni per MySQL, davvero? Non vedo alcun motivo per cui lo farebbe poiché il driver lo supporta in modo nativo. No?
netcoder

16
Lo fa. Dicono che nella documentazione non è così. Ma nel codice sorgente è chiaramente visibile e facile da correggere. Scrivo fino all'incompetenza degli sviluppatori.
Theodore R. Smith,

5
@ TheodoreR.Smith: non è così facile da risolvere. Ho lavorato per modificare il valore predefinito, ma non riesce a caricare una barca di test quando viene cambiato. Quindi è un cambiamento più grande di quanto sembri. Spero ancora di averlo finito entro il 5,5 ...
ircmaxell il

14
@shadyyx: No, la vulnerabilità di cui parlava l'articolo addslashes. Ho basato questa vulnerabilità su quella. Prova tu stesso. Vai a prendere MySQL 5.0, esegui questo exploit e guarda tu stesso. Per quanto riguarda come metterlo in PUT / GET / POST, è TRIVIAL. I dati di input sono solo flussi di byte. char(0xBF)è solo un modo leggibile di generare un byte. Ho dimostrato questa vulnerabilità dal vivo di fronte a più conferenze. Fidati di me su questo ... Ma se non lo fai, provalo tu stesso. Funziona ...
Ircmaxell,

5
@shadyyx: per quanto riguarda il passare così strano in $ _GET ... ?var=%BF%27+OR+1=1+%2F%2Anell'URL, $var = $_GET['var'];nel codice, e Bob è tuo zio.
cHao,

183

TL; DR

mysql_real_escape_string() volere fornirà alcuna protezione (e potrebbe inoltre confondere i tuoi dati) se:

  • La NO_BACKSLASH_ESCAPESmodalità SQL di MySQL è abilitata (quale potrebbe essere, a meno che non si selezioni esplicitamente un'altra modalità SQL ogni volta che ci si connette ); e

  • i valori letterali delle stringhe SQL vengono citati usando la virgoletta doppia " caratteri .

Questo è stato archiviato come bug # 72458 ed è stato corretto in MySQL v5.7.6 (vedere la sezione intitolata " The Saving Grace ", di seguito).

Questo è un altro, (forse meno?) Oscuro EDGE CASE !!!

In omaggio a all'ottima risposta di @ircmaxell (in realtà, si suppone che sia adulazione e non plagio!), Adotterò il suo formato:

L'attacco

A partire da una dimostrazione ...

mysql_query('SET SQL_MODE="NO_BACKSLASH_ESCAPES"'); // could already be set
$var = mysql_real_escape_string('" OR 1=1 -- ');
mysql_query('SELECT * FROM test WHERE name = "'.$var.'" LIMIT 1');

Ciò restituirà tutti i record dalla testtabella. Una dissezione:

  1. Selezione di una modalità SQL

    mysql_query('SET SQL_MODE="NO_BACKSLASH_ESCAPES"');

    Come documentato in String Literals :

    Esistono diversi modi per includere caratteri di citazione all'interno di una stringa:

    • Un " '" all'interno di una stringa citata con " '" può essere scritto come " ''".

    • Un " "" all'interno di una stringa citata con " "" può essere scritto come " """.

    • Precede il carattere di citazione da un carattere di escape (" \").

    • Un “ '” all'interno di una stringa citata con “ "” non ha bisogno di alcun trattamento speciale e non deve essere raddoppiato o evitato. Allo stesso modo, " "" all'interno di una stringa citata con " '" non ha bisogno di alcun trattamento speciale.

    Se la modalità SQL del server include NO_BACKSLASH_ESCAPES, la terza di queste opzioni, che è il solito approccio adottato da mysql_real_escape_string(), non è disponibile: è invece necessario utilizzare una delle prime due opzioni. Si noti che l'effetto del quarto proiettile è che si deve necessariamente conoscere il personaggio che verrà utilizzato per citare il letterale al fine di evitare di confondere i propri dati.

  2. Il payload

    " OR 1=1 -- 

    Il payload inizia questa iniezione letteralmente con il "personaggio. Nessuna codifica particolare. Nessun personaggio speciale. Nessun byte strano.

  3. mysql_real_escape_string ()

    $var = mysql_real_escape_string('" OR 1=1 -- ');

    Fortunatamente, mysql_real_escape_string()controlla la modalità SQL e regola di conseguenza il suo comportamento. Vedi libmysql.c:

    ulong STDCALL
    mysql_real_escape_string(MYSQL *mysql, char *to,const char *from,
                 ulong length)
    {
      if (mysql->server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES)
        return escape_quotes_for_mysql(mysql->charset, to, 0, from, length);
      return escape_string_for_mysql(mysql->charset, to, 0, from, length);
    }

    Quindi escape_quotes_for_mysql()viene invocata una diversa funzione sottostante, se ilNO_BACKSLASH_ESCAPES viene utilizzata la modalità SQL . Come accennato in precedenza, tale funzione deve sapere quale carattere verrà utilizzato per citare il letterale per ripeterlo senza causare la ripetizione letterale dell'altro carattere di citazione.

    Tuttavia, questa funzione presuppone arbitrariamente che la stringa verrà quotata utilizzando il 'carattere a virgoletta singola . Vedi charset.c:

    /*
      Escape apostrophes by doubling them up
    
    // [ deletia 839-845 ]
    
      DESCRIPTION
        This escapes the contents of a string by doubling up any apostrophes that
        it contains. This is used when the NO_BACKSLASH_ESCAPES SQL_MODE is in
        effect on the server.
    
    // [ deletia 852-858 ]
    */
    
    size_t escape_quotes_for_mysql(CHARSET_INFO *charset_info,
                                   char *to, size_t to_length,
                                   const char *from, size_t length)
    {
    // [ deletia 865-892 ]
    
        if (*from == '\'')
        {
          if (to + 2 > to_end)
          {
            overflow= TRUE;
            break;
          }
          *to++= '\'';
          *to++= '\'';
        }

    Quindi, lascia i "caratteri a virgoletta doppia non toccati (e raddoppia tutti i 'caratteri a virgoletta singola ) indipendentemente dal carattere reale che viene utilizzato per citare il letterale ! Nel nostro caso $varrimane esattamente la stessa come l'argomento che è stato fornito a mysql_real_escape_string()-E 'come se nessuno fuga ha avuto luogo a tutti .

  4. The Query

    mysql_query('SELECT * FROM test WHERE name = "'.$var.'" LIMIT 1');

    Qualcosa di formale, la query di rendering è:

    SELECT * FROM test WHERE name = "" OR 1=1 -- " LIMIT 1

Come diceva il mio dotto amico: congratulazioni, hai appena attaccato con successo un programma usando mysql_real_escape_string()...

Il cattivo

mysql_set_charset()non posso fare a meno, poiché ciò non ha nulla a che fare con i set di caratteri; né può mysqli::real_escape_string(), dal momento che è solo un wrapper diverso attorno a questa stessa funzione.

Il problema, se non è già ovvio, è che la chiamata a mysql_real_escape_string() non è in grado di sapere con quale personaggio verrà citato il valore letterale, che viene lasciato allo sviluppatore per decidere in un secondo momento. Quindi, in NO_BACKSLASH_ESCAPESmodalità, non c'è letteralmente modo che questa funzione possa sfuggire ad ogni input in modo sicuro per l'uso con virgolette arbitrarie (almeno, non senza raddoppiare i caratteri che non richiedono il raddoppio e quindi la confusione dei dati).

Il brutto

La situazione peggiora. NO_BACKSLASH_ESCAPESpotrebbe non essere così raro in natura a causa della necessità del suo utilizzo per la compatibilità con lo standard SQL (ad es. vedere la sezione 5.3 della specifica SQL-92 , vale a dire la <quote symbol> ::= <quote><quote>produzione grammaticale e la mancanza di un significato speciale dato alla barra rovesciata). Inoltre, il suo uso è stato esplicitamente raccomandato come soluzione alternativa al bug (da tempo risolto) descritto dal post di ircmaxell. Chissà, alcuni DBA potrebbero persino configurarlo per essere attivato di default come mezzo per scoraggiare l'uso di metodi di escape errati come addslashes().

Inoltre, la modalità SQL di una nuova connessione viene impostata dal server in base alla sua configurazione (che un SUPERutente può modificare in qualsiasi momento); pertanto, per essere certi del comportamento del server, è necessario specificare sempre in modo esplicito la modalità desiderata dopo la connessione.

La grazia salvifica

Finché imposti sempre esplicitamente la modalità SQL in modo da non includere NO_BACKSLASH_ESCAPES, o citi i letterali di stringa MySQL usando il carattere a virgoletta singola, questo bug non può allevare la sua brutta testa: rispettivamente escape_quotes_for_mysql()non verrà usato, o il suo presupposto su quali caratteri di citazione richiedono la ripetizione sarà avere ragione.

Per questo motivo, consiglio a chiunque utilizzi di NO_BACKSLASH_ESCAPESabilitare anche la ANSI_QUOTESmodalità, poiché forzerà l'uso abituale di valori letterali di stringa a virgoletta singola. Si noti che ciò non impedisce l'iniezione di SQL nel caso in cui vengano utilizzati valori letterali tra virgolette doppie, ma semplicemente riduce la probabilità che ciò accada (poiché le query normali e non dannose fallirebbero).

In DOP, sia la sua funzione equivalente PDO::quote()che l'emulatore di istruzioni preparate fanno appello, il mysql_handle_quoter()che fa esattamente questo: assicura che il letterale evaso sia citato tra virgolette singole, quindi puoi essere certo che il PDO sia sempre immune da questo errore.

A partire da MySQL v5.7.6, questo errore è stato corretto. Vedi registro delle modifiche :

Funzionalità aggiunta o modificata

Esempi sicuri

Presi insieme al bug spiegato da ircmaxell, i seguenti esempi sono completamente sicuri (supponendo che uno stia usando MySQL dopo 4.1.20, 5.0.22, 5.1.11; o che non stia usando una codifica di connessione GBK / Big5) :

mysql_set_charset($charset);
mysql_query("SET SQL_MODE=''");
$var = mysql_real_escape_string('" OR 1=1 /*');
mysql_query('SELECT * FROM test WHERE name = "'.$var.'" LIMIT 1');

... perché abbiamo selezionato esplicitamente una modalità SQL che non include NO_BACKSLASH_ESCAPES.

mysql_set_charset($charset);
$var = mysql_real_escape_string("' OR 1=1 /*");
mysql_query("SELECT * FROM test WHERE name = '$var' LIMIT 1");

... perché stiamo citando la nostra stringa letterale con virgolette singole.

$stmt = $pdo->prepare('SELECT * FROM test WHERE name = ? LIMIT 1');
$stmt->execute(["' OR 1=1 /*"]);

... perché le dichiarazioni preparate con DOP sono immuni da questa vulnerabilità (e anche quelle di ircmaxell, a condizione che si stia utilizzando PHP≥5.3.6 e che il set di caratteri sia stato impostato correttamente nel DSN o che l'emulazione delle istruzioni preparate sia stata disabilitata) .

$var  = $pdo->quote("' OR 1=1 /*");
$stmt = $pdo->query("SELECT * FROM test WHERE name = $var LIMIT 1");

... perché la quote()funzione di DOP non solo sfugge al letterale, ma lo cita anche (in 'caratteri a virgoletta singola ); notare che per evitare il bug di ircmaxell in questo caso, è necessario utilizzare PHP≥5.3.6 e aver impostato correttamente il set di caratteri nel DSN.

$stmt = $mysqli->prepare('SELECT * FROM test WHERE name = ? LIMIT 1');
$param = "' OR 1=1 /*";
$stmt->bind_param('s', $param);
$stmt->execute();

... perché le dichiarazioni preparate da MySQLi sono sicure.

Avvolgendo

Pertanto, se tu:

  • utilizzare dichiarazioni preparate native

O

  • utilizzare MySQL v5.7.6 o successivo

O

  • in aggiunta ad impiegare una delle soluzioni in di ircmaxell sintesi, l'uso di almeno uno dei seguenti:

    • DOP;
    • letterali stringa a virgoletta singola; o
    • una modalità SQL impostata esplicitamente che non include NO_BACKSLASH_ESCAPES

... allora dovresti essere completamente al sicuro (vulnerabilità al di fuori dell'ambito della fuga di stringhe da parte).


10
Quindi, TL; DR sarebbe come "esiste una modalità server mysql NO_BACKSLASH_ESCAPES che può causare un'iniezione se non si usano virgolette singole.
Il tuo senso comune

Non riesco ad accedere a bugs.mysql.com/bug.php?id=72458 ; Ho appena ricevuto una pagina di accesso negato. Viene nascosto al pubblico a causa di un problema di sicurezza? Inoltre, capisco correttamente da questa risposta che sei lo scopritore della vulnerabilità? Se è così, congratulazioni.
Mark Amery,

1
Le persone non dovrebbero usare le "stringhe in primo luogo. SQL dice che è per identificatori. Ma eh ... solo un altro esempio di MySQL che dice "vite standard, farò quello che voglio". (Fortunatamente, è possibile includere ANSI_QUOTESnella modalità per correggere la rottura delle quotazioni. Il disprezzo aperto degli standard, tuttavia, è un problema più grande che potrebbe richiedere misure più severe.)
cHao

2
@DanAllen: la mia risposta è stata un po 'più ampia, in quanto è possibile evitare questo particolare bug attraverso la quote()funzione di DOP - ma le dichiarazioni preparate sono un modo molto più sicuro e più appropriato per evitare l'iniezione in generale. Naturalmente, se hai concatenato direttamente variabili senza caratteri di escape nel tuo SQL, sei sicuramente vulnerabile all'iniezione, indipendentemente dai metodi che usi in seguito.
Eggyal

1
@eggyall: il nostro sistema si basa sul secondo esempio sicuro sopra. Ci sono errori, in cui mysql_real_escape_string è stato omesso. Risolvere i problemi in una modalità di emergenza sembra essere il percorso prudente, sperando di non essere disturbati prima delle correzioni. La mia logica sta convertendo in dichiarazioni preparate sarà un processo molto più lungo che dovrà venire dopo. Il motivo per cui le dichiarazioni preparate è più sicuro del fatto che gli errori non creano vulnerabilità? In altre parole, il 2 ° esempio sopra implementato correttamente è sicuro quanto le dichiarazioni preparate?
DanAllen,

18

Bene, non c'è davvero nulla che possa attraversarlo, tranne i %caratteri jolly. Potrebbe essere pericoloso se stai usando una LIKEdichiarazione come un utente malintenzionato potrebbe mettere %come login se non lo filtri e dovresti solo forzare la password di uno dei tuoi utenti. Le persone spesso suggeriscono di utilizzare istruzioni preparate per renderlo sicuro al 100%, poiché i dati non possono interferire con la query stessa in quel modo. Ma per domande così semplici sarebbe probabilmente più efficiente fare qualcosa del genere$login = preg_replace('/[^a-zA-Z0-9_]/', '', $login);


2
+1, ma i caratteri jolly sono per clausola LIKE, non semplice uguaglianza.
Dor

7
Con quale misura consideri un semplice sostituto more efficientrispetto all'utilizzo di istruzioni preparate? (Le istruzioni preparate funzionano sempre, la libreria può essere rapidamente corretta in caso di attacchi, non espone errori umani [come digitare erroneamente la stringa di sostituzione completa] e ha significativi vantaggi in termini di prestazioni se l'istruzione viene riutilizzata.)
MatBailie,

7
@Slava: stai effettivamente limitando nomi utente e password solo ai caratteri di parola. La maggior parte delle persone che sanno qualcosa sulla sicurezza considerano questa una cattiva idea, poiché riduce notevolmente lo spazio di ricerca. Naturalmente considererebbero anche una cattiva idea archiviare le password in chiaro nel database, ma non è necessario aggravare il problema. :)
cHao il

2
@cHao, il mio suggerimento riguarda solo gli accessi. Ovviamente non è necessario filtrare le password, mi dispiace che non sia chiaramente indicato nella mia risposta. Ma in realtà potrebbe essere una buona idea. Usare "lo spazio di un albero di pietra ignorante" invece di "a4üua3! @V \" ä90; 8f "difficile da ricordare e digitare sarebbe molto più difficile da potenziare. Anche usando un dizionario di, dì 3000 parole per aiutarti, sapendo hai usato esattamente 4 parole - sarebbero comunque circa 3,3 * 10 ^ 12 combinazioni. :)
Slava,

2
@Slava: ho già visto quell'idea prima; vedi xkcd.com/936 . Il problema è che la matematica non lo dimostra del tutto. La tua password di esempio 17 caratteri avrebbe 96 ^ 17 possibilità, e questo se dimenticassi le umlaut e ti limitassi all'ASCII stampabile. Sono circa 4,5x10 ^ 33. Stiamo parlando letteralmente di miliardi di miliardi di volte più lavoro per la forza bruta. Anche una password ASCII a 8 caratteri avrebbe 7,2x10 ^ 15 possibilità - 3 mila volte di più.
cHao,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.