PREVENCIÓN DE ATAQUES DE INYECCIÓN SQL EN PHP | EJEMPLOS DE CÓDIGO

Después de varios años de pruebas de penetración y de trabajar con equipos de desarrollo para proteger sus sistemas, he notado que no había una entrada de blog completa que diera una explicación detallada de cómo prevenir los ataques de inyección SQL en todos los lenguajes y marcos de desarrollo de aplicaciones web más populares. Por lo tanto, decidí escribir una serie de publicaciones múltiples para convertirla en la guía más completa para prevenir la inyección SQL.

Esta guía es una serie abierta de publicaciones y esta es la primera publicación. Si desea que agregue alguna tecnología, por favor, comente a continuación y estaré muy feliz de explicar la prevención de inyección SQL para esa tecnología. Esta guía vamos a analizar las siguientes tecnologías:

  • PHP
    • Laravel
    • Symfony
    • CodeIgniter
    • CakePHP
    • FuelPHP
    • Zend
  • JEE
    • JDBC
    • JPA
  • ASP.NET
    • ADO.NET
    • ADO.NET Dopper
    • NHibernate

Este primer post se centrará en el lenguaje PHP y todas sus marcos. Más artículos seguirán para tratar las otras tecnologías enumeradas.

Para aquellos que no saben qué es un ataque de inyección SQL, permítanme hacer una pequeña introducción para explicarles esto. Los ataques de inyección SQL ocurren cuando un usuario incorrecto intenta inyectar una solicitud SQL maliciosa en una solicitud legítima. El impacto del ataque de inyección SQL difiere de una situación a otra dependiendo de varios elementos relacionados con el entorno de la aplicación, y puede pasar de ser tan “simple” como la fuga de información a un control completo del servidor. No profundizaré en la forma en que un atacante podría explotar esta vulnerabilidad o cómo podemos descubrirla en las pruebas de caja negra, ya que este no es el objetivo de este post. Lo que veremos es cómo descubrirlo en el código fuente y cómo solucionarlo.

¿Cómo prevenir un ataque de inyección SQL en un código fuente PHP puro ?

Comencemos nuestro viaje con una de las tecnologías de desarrollo PHP más antiguas y populares sin ningún framework. Ahora lo primero que hay que hacer es saber cómo se ve una línea de código fuente vulnerable para poder identificar la vulnerabilidad.

Este es un ejemplo de solicitud de autenticación:

$request = "SELECT * FROM users WHERE username=". $_POST ." AND password = ". $_POST;
$result = $mysqli->query($request);

Ahora, si observa de cerca la solicitud, notará que los parámetros de usuario $_POST y $_POST se inyectan directamente en la solicitud SQL sin ningún filtrado. Al hacerlo, esta línea de código es vulnerable a la inyección SQL.

Para corregir esta vulnerabilidad, deberá filtrar los datos antes de usarlos en la solicitud sql. Para hacer esto, la mejor solución es usar las declaraciones preparadas. Aquí hay un ejemplo de cómo puede usar las instrucciones preparadas para corregir esta vulnerabilidad:

$conn = new mysqli($servername, $username, $password, $dbname);
$stmt = $conn->prepare("SELECT * FROM users WHERE username=? AND password=?");
$stmt->bind_param($username, $password);
$stmt->execute();

Buena práctica

Tiene la posibilidad de no usar la función bind_param () aquí hay un ejemplo de cómo hacer esto :

$conn = new mysqli($servername, $username, $password, $dbname);
$stmt = $conn->prepare("SELECT * FROM users WHERE username=:username AND password=:password");
$stmt-> execute(array('username'=>$_POST,'password'=>$_POST));

Después de años de analizar códigos fuente discovered he descubierto un error interesante que muchos desarrolladores hacen al corregir esta vulnerabilidad. Echa un vistazo al siguiente ejemplo:

$stmt = $conn->prepare("SELECT * FROM users WHERE username=".$_POST." AND password=".$_POST);

Si observa este ejemplo, verá que estamos utilizando las declaraciones preparadas. Sin embargo, los parámetros se inyectan directamente en la solicitud SQL. Este es un error muy común que cometen los desarrolladores, y hace que el código sea vulnerable a la inyección SQL incluso si usamos las instrucciones preparadas.

La idea detrás de las instrucciones preparadas es filtrar los parámetros antes de inyectarlos en la solicitud SQL keep tenga esto en mente.

Nota muy importante:

PHP ofrece algunas otras funciones para filtrar solicitudes SQL y evitar la inyección SQL, pero algunas de ellas no son eficientes. La función mysql_real_escape_string (), por ejemplo, es una de las funciones PHP más conocidas para evitar la inyección SQL. Desafortunadamente, esta función es realmente eficiente. Al usar esta función, la biblioteca MySQL agregará una barra invertida a los siguientes caracteres: NULL, \ x00, \ n, \ r,\,’, ” y \ x1a. Sin embargo, para una situación como esta :

$id = mysql_real_escape_string("1 OR 1=1");
$request = "SELECT * FROM users WHERE id = $id";

En color rojo es lo que un mal usuario te enviará

La mysql_real_escape_string () no te protegerá de tal ataque.

Por eso siempre recomiendo usar las instrucciones preparadas en lugar de esta función.

¿Cómo prevenir un ataque de inyección SQL en Laravel framework ?

Ahora que hemos visto el ataque de inyección SQL contra un código PHP Puro, ahora es el momento de ver cómo podemos solucionar la vulnerabilidad en una aplicación web basada en framework. Antes de empezar a hablar de esto, permítanme primero darles una pequeña descripción del marco Laravel.

Laravel es un framework web de código abierto escrito en PHP respetando el principio modelo-vista-controlador y desarrollado completamente en programación orientada a objetos.

Ejemplo 1

Aquí hay un ejemplo de una solicitud SQL vulnerable en un laravel :

$user = DB::select('select * from users where username='. $request->post('username').' AND password='. $request->post('password'));

El framework Laravel ofrece algunas funciones increíbles para ayudar a los desarrolladores a proteger la solicitud SQL contra inyecciones maliciosas. Desafortunadamente, la mayoría de las vulnerabilidades de inyección SQL que descubro en tales aplicaciones están relacionadas con el mal uso de las funciones de Laravel.

Analicemos el ejemplo anterior, de acuerdo con la documentación de Laravel, la función DB::select podría recibir dos parámetros. El primero es la petición SQL y el segundo son los parámetros. Por lo tanto, para filtrar los parámetros de inyección SQL, deberá insertar los parámetros en la solicitud utilizando el segundo parámetro, como en este ejemplo:

user user = DB:: select (‘select * from users where username=? Y contraseña=?’, );

Ejemplo 2

Laravel tiene múltiples formas de comunicarse con la base de datos en este ejemplo verá que laravel obliga al desarrollador a usar algunas funciones que filtran automáticamente los datos del usuario, como el siguiente ejemplo:

Users::where('username', $request->get('username'))->orderBy($request->get('orderby'))->get();

Se supone que esta función está protegida contra inyección SQL y lo están, el único problema está en el nivel de función OrderBy (). De acuerdo con la documentación de Laravel, esta función no filtra los datos del usuario, por lo que aún es posible un ataque de inyección SQL a través de esta función.

Lo mejor que puede hacer es no darle al usuario la posibilidad de controlar el nombre de la tabla, pero si esto es algo inevitable, entonces necesitará usar una lista blanca para validar los datos del usuario antes de insertarlos en esa función.

¿Cómo prevenir un ataque de inyección SQL en Symfony?

Veamos otro conocido framework PHP que ofrece un conjunto de componentes PHP reutilizables para acelerar el desarrollo de aplicaciones PHP. Symfony también es utilizado por algunos de los CMS web más conocidos como Drupal y Magento.

Symfony es uno de los frameworks más seguros con los que puedes trabajar, ofrece un amplio número de funciones para escribir un código seguro. Sin embargo, si esas funciones no se usan bien, entonces obtienes un código vulnerable. Así que aquí hay un ejemplo de un código vulnerable:

$entityManager = $this->getEntityManager();
$query = $entityManager->createQuery('SELECT p
FROM App\Entity\Product p
WHERE p.price > '. $request->query->get('price')
);

Ahora analicemos este código vulnerable. De acuerdo con la documentación de Symfony, createQuery() es una función que utiliza por defecto las instrucciones preparadas. Por lo tanto, el objeto resultante de dicha función le da acceso a la función setParameter (). Este filtra todos los datos de usuario para evitar una inyección SQL. Aquí hay un ejemplo de cómo usarlo :

$entityManager = $this->getEntityManager();
$query = $entityManager->createQuery('SELECT p
FROM App\Entity\Product p
WHERE p.price > :price
ORDER BY p.price ASC'
)->setParameter('price', $price);

¿Cómo prevenir un ataque de inyección SQL en Codeigniter?

CodeIgniter uno de los frameworks PHP más potentes, ligeros y populares con un tamaño muy pequeño. Fue creado para desarrolladores que necesitan un kit de herramientas simple y elegante para crear aplicaciones web con todas las funciones. Al igual que Symfony y Laravel, Codeigniter también viene con algunos sistemas de seguridad para ayudar a los desarrolladores a crear aplicaciones más seguras.

Sin embargo, también con Codeigniter algunos desarrolladores cometen el mismo error e inyectan los datos del usuario sin ningún filtrado. Este es un ejemplo de estos errores que conducen a un ataque de inyección SQL:

$query = 'SELECT * FROM users WHERE username = '. $this-> input->post('username'). ' AND password= '. $this-> input->post('password');
$this->db->query($query);

Ahora para solucionar este problema, tendrá que echar un vistazo a la documentación de Codeigniter. Según ella, la función query () toma dos parámetros. La primera es la consulta sql, y la segunda son los parámetros que desea vincular.

Ejemplo 1

De forma predeterminada, esta función filtra todos los parámetros enlazados para evitar inyecciones SQL. Aquí es un ejemplo de la manera correcta de usar esta función:

$query = 'SELECT * FROM users WHERE username = ? AND password = ? ';
$this->db->query($query, array($this->input->post('username'), $this-> input-> post('password')));

Ejemplo 2:

Codeigniter proporcionar otra forma de realizar SQL solicitud de prevenir inyecciones SQL. Este es un ejemplo de uso de la clase Active Record para evitar inyecciones SQL:

$this->db->get_where('users',array('username'=>$this->input->post('username') ,'password' => $this->input->post('password')));

La función get_where () ya no existe en la versión 4 de Codeigniter.

Nota 1

Codeigniter también ofrece una función que se parece a, mysql_real_escape_string () función llamada escape (). Pero, como dije al principio, mysql_real_escape_string () podría ser omitido en algunos casos y necesitas evitar usarlo. El hecho de que la función escape() haga exactamente lo mismo, entonces también sería posible omitirla. Es por eso que no animo a los desarrolladores a usar la función escape ().

Nota 2

¿Cómo prevenir un ataque de inyección SQL en CakePHP ?

CakePHP es un framework de desarrollo rápido para PHP que utiliza patrones de diseño comúnmente conocidos como Mapeo de Datos Asociativos, Controlador Frontal y MVC. CakePHP ofrece un montón de componentes incorporados para facilitar el desarrollo de aplicaciones web y las hará más seguras.

Sin embargo, las aplicaciones CakePHP también son vulnerables a las inyecciones SQL si no se usan bien. Este es un ejemplo de un ataque de código vulnerable a inyección SQL en este marco:

$results = $connection-> execute('SELECT * FROM users WHERE username = '. $this->request->getParam('username').' AND password='. $this->request->getParam('password'))->fetchAll('assoc');

La función execute() utiliza de forma predeterminada las instrucciones preparadas. Sin embargo, el ejemplo anterior sigue siendo vulnerable al ataque de inyección SQL, ya que los datos del usuario se insertan directamente en una solicitud sql legítima. Aquí hay un ejemplo de la forma correcta de usar esta función:

$results = $connection->execute('SELECT * FROM users WHERE username = :username AND password=:password', )->fetchAll('assoc');

El framework CakePHP también ofrece un sistema llamado Query Builder, que fuerza el filtrado de datos de usuario para evitar ataques de inyección SQL. De acuerdo con la documentación de CakePHP, Debajo de las carátulas, el Generador de consultas usa las instrucciones preparadas.

Aquí hay un ejemplo de cómo puede usar dicho sistema de la manera correcta:

use Cake\ORM\Locator\LocatorAwareTrait;
$users = $this->getTableLocator()->get('users');
// Start a new query.
$query = $users->find();
$query->where();

¿Cómo prevenir un ataque de inyección SQL en FuelPHP ?

FuelPHP es uno de los framework PHP más recientes que nace a partir de las mejores ideas de cada framework del mercado. Fue desarrollado con PHP 5 y está totalmente orientado a objetos. En FuelPHP, la seguridad era la principal preocupación, lo que impulsó a sus colaboradores a implementar muchos mecanismos de seguridad para filtrar los datos de los usuarios. Ataques de inyección SQL una de las razones para implementar este tipo de mecanismos en FuelPHP.

Sin embargo, incluso con un marco tan poderoso, un simple mal uso de esos mecanismos puso todo el código en peligro para un ataque de inyección SQL. Este es un ejemplo de error de código:

$query = "SELECT * FROM article WHERE id = ". Input::get('id');
$result = DB::query($query)->execute();

Para solucionar esto, también hay dos técnicas como los otros marcos. La primera es usar correctamente la función query (), vinculando los parámetros como el siguiente ejemplo :

$query = "SELECT * FROM article WHERE id = :id"; // our query
$result = DB::query($query)->bind('id', Input::get('id'))->execute();

La segunda solución es utilizar el mecanismo implemented implementado en el marco FuelPHP para comunicarse con la base de datos. Aquí hay un ejemplo de cómo usarlo:

$user = DB::select()->from('article')->where('id', Input::get('id'))->execute();

¿Cómo prevenir un ataque de inyección SQL en zend framework?

Zend framework (cambiado a Proyecto Laminas) es uno de los framework más conocidos en el mundo de PHP. Se desarrolló pensando en el ajuste de rendimiento, lo que explica por qué cada nueva versión es mucho más rápida que la anterior. Zend también se desarrolló con las mejores prácticas de seguridad, lo que impulsó a sus desarrolladores a implementar mecanismos de seguridad adicionales para hacer frente a las amenazas cibernéticas conocidas.

Algunos de estos mecanismos se implementaron para hacer frente a los ataques de inyección SQL. Pero como siempre, un mal uso de esos mecanismos conduce a un código vulnerable. En esta parte del artículo me voy a mostrar un ejemplo de este tipo de error:

$adapter->query('SELECT * FROM `article` WHERE `id` = '. $this->getRequest()->getPost('id'));

Aquí es cómo solucionar esta vulnerabilidad:

$adapter->query('SELECT * FROM `article` WHERE `id` = ?', );

Leave a Reply