2010-03-29 9 views
5

Sto costruendo una classe di database e ho pensato che sarebbe una buona idea incorporare una qualche forma di prevenzione dell'iniezione SQL (duh!). Ecco il metodo che esegue una query di database:Prevenzione dell'iniezione SQL in una classe di database

class DB 
{ 
    var $db_host = 'localhost'; 
    var $db_user = 'root'; 
    var $db_passwd = ''; 
    var $db_name = 'whatever'; 

    function query($sql) 
    { 
     $this->result = mysql_query($sql, $this->link); 
     if(!$this->result) 
     { 
      $this->error(mysql_error()); 
     } else { 
      return $this->result; 
     } 
    } 
} 

C'è di più nella classe di quello ma sto tagliando verso il basso solo per questo. Il problema che sto affrontando è che se utilizzo semplicemente lo mysql_real_escape_string($sql, $this->link);, esso sfugge all'intera query e porta a un errore di sintassi SQL. Come posso trovare dinamicamente le variabili che devono essere sfuggite? Voglio evitare di usare mysql_real_escape_string() nei miei blocchi di codice principale, preferisco averlo in una funzione.

Grazie.

+4

So che ho detto questo sul tuo post precedente, ma fammi ripetere: Usa PDO. Se usi PDO con le sue istruzioni preparate, non devi nemmeno preoccuparti di sfuggirle. – ryeguy

+0

Utilizzare i parametri parametrizzati, utilizzare o adodb o pdo. – rook

risposta

0

L'intera idea di impedire attacchi di SQL injection è impedire agli utenti di eseguire il proprio SQL. Qui sembra che tu voglia consentire agli utenti di eseguire il proprio SQL, quindi perché limitarli?

Oppure se non si consente agli utenti di passare SQL nel metodo query(). Questo è troppo basso per implementare i parametri di escape. Come hai capito, vuoi sfuggire solo i parametri, non le istruzioni SQL complete.

Se si parametrizza l'SQL, è possibile evitare solo i parametri. In questo caso, suppongo che gli utenti possano influenzare i valori dei parametri e non l'SQL.

Dai un'occhiata ai metodi bindParam() o Zend_DBdi Zend_DB per avere un'idea di come è implementato in altre interfacce di database.

+0

Se questo verrà utilizzato nei moduli di accesso e simili (sarà), non voglio dovermi fidare che l'utente inserisca SQL non dannoso. – Joe

+0

È chiaramente una classe che intende riutilizzare per tutto il suo codice ... –

+0

"verrà utilizzato nei moduli di accesso"? Attenzione per Bobby Tables ... http://xkcd.com/327/ – Simon

0

Per fare questo nel modo giusto, è necessario disinfettare la roba, come si sta costruendo la query, o mettere bene in classe per passare parametri indipendenti della query nucleo.

1

Il problema è che nel momento in cui avete costruito una query SQL che sia troppo tardi per essere in grado di evitare l'iniezione trovando variabili - altrimenti sarebbe già essere costruito in PHP.

L'escape deve essere eseguito molto prima quando si creano le query. È possibile utilizzare una classe di creazione di query.

Tuttavia Consiglio un approccio diverso - che è quello di avere uno strato che fornisce una tabella di database come oggetto, qui è un esempio user object che deriva dal base db entity class che fornisce un'interfaccia completa ad un database utilizzando active record pattern, e lo iterator pattern.

Illustrerò questo con alcuni esempi; la cosa migliore qui è quella degli iteratori, dal momento che puoi astrarre molto di più e avere alcune classi piuttosto generiche più avanti sulla linea per estrarre i dati.

Per creare un record utente utilizzando l'approccio di cui sopra:

$user = new DbUser(); 
$user->create(); 
$user->set_email('[email protected]'); 
$user->write(); 

Per leggere un record utente:

$user = new DbUser(); 
$user->set_email('[email protected]'); 
if ($user->load_from_fields()) 
{ 
} 

per scorrere i record:

$user_iterator = DbUser::begin(); 
if ($user_iterator->begin()) 
{ 
    do 
    { 
     $user = $user_iterator->current(); 
     echo $user->get_email(); 
    } while ($user_iterator->next()); 
} 
+1

Questa non è una cattiva idea. Le parole chiave qui sono "pattern di registrazione attivo". Zend Framework, Ruby on Rails e alcuni altri framework forniscono questo tipo di funzionalità. –

+0

E dare un'occhiata ad alcune delle discussioni su orm/active qui su stackoverflow, ad es. http://stackoverflow.com/questions/494816/using-an-orm-or-plain-sql – VolkerK

1

ci sono due approcci per prevenire un attacco di SQL injection: approccio basato sulla lista nera e approccio basato sulla whitelist.

L'approccio con lista nera implica che è necessario controllare l'intera stringa di query e identificare il codice indesiderato e rimuoverlo. Questo è molto difficile. Invece, utilizzare l'approccio white-list usando l'SQL parametrizzato. In questo modo sarai sicuro che l'unica query che verrà eseguita sarà quella che hai intenzionalmente creato usando il tuo codice, e qualsiasi tentativo di iniezione fallirà poiché tutte le query di iniezione saranno parte del parametro e quindi non saranno eseguite dal Banca dati. Stai cercando di trovare un modo per prevenire l'iniezione dopo che la query è già stata creata, il che significa indirettamente un approccio basato sulla lista nera.

Provare a utilizzare il codice SQL parametrizzato nel codice, uno dei principi di codifica sicuri adattati a livello globale.

1

Sia parametrizzata o provare a costruire la classe DB al fine di superare tutti i valori per WHERE (e tutto ciò che può utilizzare i valori) utilizzando un qualcosa di simile a:

$db->where(x,y); 

es.

$db->where('userid','22'); 

E nei corpus di classe usare qualcosa come

function where(var x, var y) // method 
{ 
    $this->where .= x . ' = '.mysql_real_escape_string(y); 
} 

Naturalmente questo deve essere pulita per supportare più DOVE ingressi.

0

Io, ho risolto questo problema aggiungendo parametri alla funzione query. Ho scoperto che il codeigniter lo ha fatto molto bene, quindi l'ho adattato ai miei gusti.

Esempio:

$result = Database::query('INSERT INTO table (column1,column2,column3) VALUES(?,?,?)',array($value1,$value2,$value3)); 



public static $bind_marker = '?'; 
public static function query($query, $binds = FALSE) 
    { 
     if($binds !== FALSE) 
     { 
      $query = self::compile_binds($query,$binds); 
     } 
     // $query now should be safe to execute 
} 

private static function compile_binds($query, $binds) 
    { 
     if(strpos($query, self::$bind_marker) === FALSE) 
     { 
      return $query; 
     } 

     if(!is_array($binds)) 
     { 
      $binds = array($binds); 
     } 

     $segments = explode(self::$bind_marker, $query); 

     if(count($binds) >= count($segments)) 
     { 
      $binds = array_slice($binds, 0, count($segments)-1); 
     } 

     $result = $segments[0]; 
     $i = 0; 
     foreach($binds as $bind) 
     { 
      if(is_array($bind)) 
      { 
       $bind = self::sanitize($bind); 
       $result .= implode(',',$bind); 
      } 
      else 
      { 
       $result .= self::sanitize($bind); 
      } 

      $result .= $segments[++$i]; 
     } 

     return $result; 
    } 

public static function sanitize($variable) 
{ 
    if(is_array($variable)) 
    { 
     foreach($variable as &$value) 
     { 
      $value = self::sanitize($value); 
     } 
    } 
    elseif(is_string($variable)) 
    { 
     mysql_real_escape_string($variable); 
    } 
    return $variable; 
} 

L'aggiunta importante aggiunta dalla versione di codeigniter è posso usare un array come parametro che è utile per l'utilizzo "IN":

$parameters = array 
    (
     'admin', 
     array(1,2,3,4,5) 
    ); 

$result = Database::query("SELECT * FROM table WHERE account_type = ? AND account_id IN (?)",$parameters); 
1

Il punto di la prevenzione dell'iniezione di codice è che si desidera distinguere tra sql e sql che gli utenti hanno iniettato. Non puoi farlo più a questo livello più basso. Consente di dare un esempio:

select * from users where username='test' and password='itisme' or '4'='4' 

Questo sembra perfetto SQL valida, ma può anche essere uno SQL iniettato versione di:

"select * from users where username='test' and password='" . "itisme' or '4'='4". "'" 

quindi bisogna farlo più in alto nel codice, o l'uso involucri come suggerivano gli altri.

0

Perché reinventare la ruota? Basta estendere PDO e utilizzare le query con parametri. Se non sai di cosa si tratta, leggi il documento che contiene molti esempi per iniziare.

Problemi correlati