Prevenzione dell'iniezione SQL più veloce

10

Mi sono guardato intorno ovunque e non riesco a trovare la risposta alla mia domanda.

Sto usando il più recente PHP per scripting lato server e MySQL per il mio database. Il set di caratteri è utf8mb4 se questo fa la differenza.

Fino ad ora, ho utilizzato istruzioni preparate per proteggermi dall'iniezione SQL. Tuttavia, poiché la maggior parte delle mie query viene eseguita una sola volta, il codice è in realtà lento . Quando uso $conn->query() invece, e $conn->real_escape_string() per tutte le mie variabili, è più veloce.

Tuttavia, $conn->real_escape_string() è anche lento perché c'è un andata e ritorno ad ogni chiamata. Ma non capisco perché sia necessario un viaggio di andata e ritorno perché mi sembra che la funzione di escape possa essere programmata in PHP.

Dai documenti, una funzione sarebbe simile a questa:

Characters encoded are NUL (ASCII 0), \n, \r, \, ', ", and Control-Z.

function sanitize($str)
{
    str_replace(array('\', "
$sql = "UPDATE ipsum SET lorem=$str WHERE id=1337";
", "\n", "\r", "'", '"', "\x1a"), array('\\', '\0', '\n', '\r', "\'", '\"', '\Z'), $str); }

Supponendo che ho fatto in modo che sia nella giusta codifica e che non sia vuoto.

Questo è stato pubblicato come commento in uno dei documenti. A proposito, str_replace funziona con Unicode.

Tuttavia, ho pensato a qualcosa che ha un po 'più senso. Guardando la pagina , posso allungare questo lotto ulteriore. Per una dichiarazione come questa:

function sanitize($str)
{
    return "'" .str_replace(array("\", "'"), array("\\", "\'"), $str) . "'";
}

Non posso semplicemente farlo (e sto verificando in anticipo per assicurarmi che sia un UTF-8):

function sanitize($str)
{
    str_replace(array('\', "
$sql = "UPDATE ipsum SET lorem=$str WHERE id=1337";
", "\n", "\r", "'", '"', "\x1a"), array('\\', '\0', '\n', '\r', "\'", '\"', '\Z'), $str); }

E questo preverrà le iniezioni SQL? Perchè no? Non riesco a pensare a un input per il quale fallirà.

In caso contrario, esiste una migliore funzione PHP che può proteggere meglio dall'iniezione SQL?

Grazie in anticipo! Puoi testare SQL qui .

UPDATE: ha ricevuto le risposte che mi aspettavo. Per chiarire, non ti sto chiedendo di dare la tua classica predica di "dichiarazioni preparate sono la cosa migliore che sia mai fatta o morire". Se lo volessi, chiederei a Yahoo! Risposte e non qui.

Mi piacerebbe sapere perché il mio metodo (il secondo) non è sicuro (o se lo è). Sarebbe anche bello includere una discussione su cosa real_escape_string o istruzioni preparate effettivamente fanno per disinfettare i dati. Sia MySQL che PHP sono open source e sono sicuro che qualcuno sa cosa sta succedendo quando vengono chiamate queste funzioni.

Semplicemente non capisco perché sia necessario un viaggio di andata e ritorno per disinfettare le corde. Come può un metodo come quello non essere implementato in PHP?

UPDATE 2: Ho iterato attraverso tutti i caratteri unicode (da 0x0000 a 0x1F77F, trovato su Wikipedia) e ho notato che, se real_escape_string() scappa solo caratteri singoli e non frasi (che secondo i documenti che fa), sotto utf8mb4 charset, i caratteri che cambiano sono:

unicode 0 => %bl0ck_qu0te%

unicode 10 =>\n

unicode 13 =>\r

unicode 26 =>\Z

unicode 34 =>\"

unicode 39 =>\'

unicode 92 =>\

Quindi, anche se è utf8mb4 , non è diverso da quello che dicono i documenti (suppongo perché UTF-8 è standard). Quindi, perché non è implementabile in PHP?

Ecco uno script PHP che combina tutti questi unicode e li disinfetta. Ecco un cookie di Windows .

    
posta Shahar 31.08.2014 - 21:42
fonte

6 risposte

13

Riguardo alla tua funzione di sanitizzazione - mentre non riesco a produrre un exploit per l'esatto esempio che hai fornito, se cambiamo il tuo esempio in qualcosa di simile:

$sql = "UPDATE ipsum SET price=$str WHERE id=1337";

Se un valore di 10, otherColumn=1234 o forse 10;-- è stato passato per $str potresti vedere problemi. Se si esegue la propria funzione di disinfezione e si inizia a utilizzarla nel codice, sembra che sarebbe solo una questione di tempo prima che si verificasse una vulnerabilità come questa. Continuerò a giocare per vedere se riesco a trovare un exploit per l'esatto esempio che hai fornito nella tua domanda.

Detto questo, non consiglierei di eseguire la tua funzione di sanitizzazione. Le persone più intelligenti di te e io hanno compiuto notevoli sforzi per testare l'efficacia dello standard industriale attualmente accettato sull'uso di istruzioni preparate per prevenire SQL Injection in modo da poter essere (abbastanza) certo che sia sicuro. Rotolare il tuo è quasi sempre un errore.

Inoltre, scommetto che ci sono persone là fuori che usano istruzioni preparate in ambienti che gestiscono più traffico e richiedono più prestazioni del tuo progetto attuale.

Se riscontri problemi di prestazioni durante l'accesso al tuo livello dati, ti consiglio di dedicare un po 'di tempo a esaminare l'ottimizzazione delle prestazioni piuttosto che creare la tua funzione di disinfezione.

L'accesso all'ottimizzazione delle prestazioni per il livello dati è una parte essenziale e normale dello sviluppo. Rolling your own sanitization no.

    
risposta data 01.09.2014 - 04:51
fonte
6

In primo luogo, PHP ha già la tua seconda funzione e si chiama addslashes () .

I documenti dicono esplicitamente:

To escape database parameters, DBMS specific escape function (e.g. mysqli_real_escape_string() for MySQL or pg_escape_literal(), pg_escape_string() for PostgreSQL) should be used for security reasons.

If your DBMS doesn't have an escape function and the DBMS uses \ to escape special chars, you might be able to use this function only when this escape method is adequate for your database. Please note that use of addslashes() for database parameter escaping can be cause of security issues on most databases.

La tua prima funzione manca anche diversi caratteri che sono significativi per la sintassi di MySQL.

Altri importanti motivi per cui non dovresti ricorrere a questo:

  • Supponiamo che tu possa fare a meno di una sanitizzazione più efficiente ma meno completa per query specifiche. Vuoi provare e considerare tutti i potenziali vettori di attacco contro ogni singola query che scrivi? Quanto tempo finché non commetti un errore?

  • Un buon motivo per usare le istruzioni preparate su mysql_real_escape_string è che un giorno ti dimenticheresti inevitabilmente di scappare da qualcosa. Molte iniezioni SQL in progetti PHP ben noti sono state causate da qualcuno che semplicemente dimentica di sfuggire a qualcosa, succede a tutti. Almeno con le dichiarazioni preparate se ti dimentichi di chiamare bindParam () la tua query non funziona.

Se vuoi migliorare le prestazioni implementa il caching ed evita completamente le query MySQL.

Inoltre, assicurati di utilizzare una versione aggiornata di MySQL:

Before 5.1.17, the query cache is not used for prepared statements. http://dev.mysql.com/doc/refman/5.1/en/query-cache.html

Non fare il tuo giro quando si tratta di sicurezza.

    
risposta data 01.09.2014 - 01:00
fonte
4

Il tuo codice non funziona:

  • se il charset di connessione predefinito sul server MySQL non è un superset ASCII rigido, ad esempio se è Shift-JIS (che può contraffare il byte per il backslash nel secondo byte di una sequenza multibyte);

  • se il server MySQL è configurato per utilizzare l'opzione sql_mode no_backslash_escape (come potrebbe essere per l'interoperabilità perché i backslash escape sono standard non ANSI).

In entrambi questi casi c'è un input che può sfuggire a una stringa letterale con potenziali conseguenze per la sicurezza del disastro.

Quindi potrebbe essere OK per la tua configurazione attuale, ma è abbastanza fragile per chiunque altro da qualsiasi altra parte.

    
risposta data 01.09.2014 - 14:46
fonte
4

Riesci a rimuovere i freni della tua auto per risparmiare peso? Beh, se guidi su un sito isolato e tieni la velocità al minimo e assicurati che non succeda mai nulla di straordinario (come una persona disinformata che usa l'auto), probabilmente .

Il tuo metodo "funziona" finché controlli ogni singolo aspetto del sistema e non trascuri nulla. Hai già fallito nel soddisfare il secondo requisito (non conoscevi l'impostazione NO_BACKSLASH_ESCAPE ), quindi cosa ti fa pensare che ora tutto sia a posto? Sei sicuro di non aver trascurato un'altra impostazione? Puoi promettere che chiunque lavori sul sistema ora e in futuro conosce gli strani vincoli e non farà mai un errore?

Personalmente, non scommetterei i miei soldi su di esso. Quando scriviamo applicazioni per il mondo reale, vogliamo che funzionino in tutte condizioni, anche se non controlliamo ogni singola impostazione, anche se qualcuno commette un errore, anche se trascuriamo alcuni dettagli. In altre parole, la soluzione dovrebbe essere robusta , perché molti decenni di sviluppo del software hanno dimostrato che gli esseri umani sono in effetti fallibili. Le dichiarazioni preparate sono molto robuste, mysql_real_escape_string() è anche abbastanza robusto. La tua funzione no. A tutti.

Mi chiedo anche come hai concluso che entrambe le istruzioni preparate e mysql_real_escape_string() sono "troppo lente". Se hai così tanti utenti che devi preoccuparti di alcuni viaggi extra, allora l'ultima cosa che vuoi è uno strumento di sicurezza fatto in casa. E se non hai tanti utenti, allora penso che stai semplicemente esagerando.

    
risposta data 01.09.2014 - 18:14
fonte
2

L'escaping in generale ha ha avuto problemi .

Utilizza istruzioni preparate , invece di sfuggire a qualsiasi cosa. È l'unico modo corretto per passare i parametri alle query SQL. Sono abbastanza sicuro che sia anche il più veloce, dal momento che è solo il passaggio dei valori al server sql.

    
risposta data 01.09.2014 - 17:20
fonte
-1

L'uso del proprio disinfettante non è una buona scelta quando esiste una funzione predefinita per questo. in secondo luogo per evitare l'iniezione sql è possibile utilizzare firewall o filtri a livello di applicazione. per esempio puoi caricare mod_security nel server Apache per prevenire gli attacchi sqli.

    
risposta data 01.09.2014 - 14:49
fonte

Leggi altre domande sui tag