2010-03-29 6 views
5

Estoy construyendo una clase de base de datos y pensé que sería una buena idea incorporar algún tipo de prevención de inyección SQL (duh!). Aquí está el método que se ejecuta una consulta de base de datos:Impedir la inyección de SQL en una clase de base de datos

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

Hay más en la clase que eso, pero estoy a cortarlo sólo para esto. El problema al que me enfrento es si solo uso mysql_real_escape_string($sql, $this->link);, entonces escapa de toda la consulta y conduce a un error de sintaxis SQL. ¿Cómo puedo encontrar dinámicamente las variables que necesitan ser escapadas? Quiero evitar el uso de mysql_real_escape_string() en mis bloques de código principales, prefiero tenerlo en una función.

Gracias.

+4

Sé que dije esto en su publicación anterior, pero permítame repetir: Use PDO. Si usa PDO con sus declaraciones preparadas, ni siquiera tiene que preocuparse por escapar de ellas. – ryeguy

+0

Utilice las especificaciones parametrizadas, use adodb o pdo. – rook

Respuesta

0

La idea de evitar ataques de inyección SQL es evitar que los usuarios ejecuten su propio SQL. Aquí, parece que desea permitir a los usuarios ejecutar su propio SQL, entonces, ¿por qué limitarlos?

O si no está permitiendo que los usuarios pasen SQL al método query(). Este es un nivel demasiado bajo para implementar parámetros de escape. Como se habrá dado cuenta, solo quiere escanear parámetros, no todas las declaraciones SQL.

Si parametriza el SQL, puede escapar solo de los parámetros. En este caso, supongo que los usuarios pueden afectar los valores de los parámetros, y no el SQL.

Eche un vistazo a los métodos bindParam() de la DOP o los query() de Zend_DB para tener una idea de cómo esto se implementa en otras interfaces de base de datos.

+0

Si esto se va a usar en formularios de inicio de sesión y similares (lo será) no quiero tener que confiar en que el usuario ingrese SQL no malicioso. – Joe

+0

Es claramente una clase que planea reutilizar a lo largo de su código ... –

+0

"va a ser utilizado en los formularios de inicio de sesión"? Cuidado con Bobby Tables ... http://xkcd.com/327/ – Simon

0

Para hacer esto de la manera correcta, tiene que desinfectar las cosas a medida que crea la consulta, o poner eso en su clase para pasar parámetros independientes de la consulta principal.

1

El problema es que cuando se ha creado una consulta SQL, es demasiado tarde para poder evitar la inyección mediante la búsqueda de variables; de lo contrario, ya estaría integrada en PHP.

El escape debe hacerse mucho antes al generar las consultas. Podría usar una clase de creación de consultas.

Sin embargo, recomendaría un enfoque diferente - que es tener una capa que proporciona una tabla de base de datos como un objeto, aquí es un ejemplo user object que se deriva de la base db entity class que proporciona una interfaz completa a una base de datos utilizando active record pattern, y el iterator pattern.

Lo ilustraré con algunos ejemplos; Lo bueno de esto es iteradores, ya que puede abstraer mucho más y tener algunas clases bastante genéricas más adelante en la línea para extraer los datos.

Para crear un registro de usuario utilizando el enfoque anterior:

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

Para leer un registro de usuario:

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

para iterar a través de registros:

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

Esto no es una mala idea. Las palabras clave aquí son "patrón de registro activo". Zend Framework, Ruby on Rails y algunos otros marcos proporcionan este tipo de funcionalidad. –

+0

Eche un vistazo a algunas de las discusiones de orm/active record aquí en stackoverflow, p. http://stackoverflow.com/questions/494816/using-an-orm-or-plain-sql – VolkerK

1

Hay dos enfoques para evitar un ataque de inyección SQL: enfoque basado en lista negra y enfoque basado en lista blanca.

El enfoque de lista negra implica que debe verificar toda la cadena de consulta e identificar el código no deseado y eliminarlo. Esto es mucho más difícil. En su lugar, utilice el enfoque de lista blanca mediante el uso de SQL parametrizado. De esta forma, se asegurará de que la única consulta que se ejecutará será la que construyó intencionadamente utilizando su código, y cualquier intento de inyección fallará, ya que todas las consultas de inyección formarán parte del parámetro y, por lo tanto, no se ejecutará por el base de datos. Está tratando de encontrar una forma de evitar la inyección después de que la consulta ya se haya creado, lo que indirectamente significa un enfoque basado en la lista negra.

Trate de usar SQL parametrizado en su código, es uno de los principios de codificación segura adaptados globalmente.

1

De cualquier parametrizada o tratar la construcción de la clase DB con el fin de pasar todos los valores de dónde (y cualquiera que utilice valores) utilizando un algo como:

$db->where(x,y); 

decir.

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

Y en los corpus de clase usar algo como

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

Por supuesto, esto requiere limpieza para soportar múltiples donde los insumos.

0

Yo resolví este problema agregando parámetros a la función de consulta. Encontré que Codeigniter lo hizo muy bien, así que lo adapté a mis propios gustos.

Ejemplo:

$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; 
} 

La adición importante añadí partir de la versión de CodeIgniter es yo puede utilizar una matriz como un parámetro que es útil para el uso de "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

El punto de La prevención de inyección de código es que desea diferenciar entre su sql y sql que los usuarios inyectaron. No puedes hacer eso más en este nivel más bajo. Vamos a dar un ejemplo:

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

Esto parece perfecto SQL válida, pero también puede ser un SQL inyectado versión de:

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

Así que hay que hacerlo más arriba en su código, o el uso envoltorios como los otros sugirieron.

0

¿Por qué reinventar la rueda? Simplemente extienda PDO y use consultas parametrizadas. Si no sabe de qué se trata, lea el documento que contiene muchos ejemplos para comenzar.

Cuestiones relacionadas