Mitigación del ataque de inyección SQL mediante declaraciones preparadas (consultas parametrizadas)

Como se explica en este artículo , un ataque de inyección SQL, o SQLi, es una forma de explotar la vulnerabilidad subyacente de una instrucción SQL mediante la inserción de instrucciones SQL nefastas en su campo de entrada para su ejecución. Apareció por primera vez en 1998 y, desde entonces, se dirige principalmente a minoristas y cuentas bancarias. Cuando se combina con otras formas de ataques, como ataques DDOS, secuencias de comandos entre sitios (XSS) o secuestro de DNS, puede generar resultados a gran escala. 
Terminología:
 

  • Validación: la validación es el proceso de verificar si la entrada cumple con un conjunto de criterios (como una string que no contiene comillas simples independientes). 
     
  • Limpieza: la limpieza es el proceso de modificar la entrada para garantizar que sea válida (como duplicar comillas simples). 
     

Para evitar la inyección de SQL, todas las entradas que se van a concatenar en SQL dinámico deben filtrarse y desinfectarse correctamente.
Anatomía de un ataque SQL: 
un ataque SQL tiene las siguientes dos partes:
 

  • Investigación: vea las partes vulnerables de la aplicación del usuario final que se conectan con la base de datos. 
     
  • Ataque: ingrese campos maliciosos que pueden modificar la consulta para su propio beneficio. 
     

No_WAF

Ejemplo 1: 
Considere la siguiente pieza de código para un formulario de autenticación escrito en Java:
 

Java

String query = "SELECT userName, balance FROM accounts"
    + "WHERE userID=" + request.getParameter("userID") +
  "and password='" + request.getParameter("Password") + "'";
 
try
{
    Statement statement = connection.createStatement();
    ResultSet rs = statement.executeQuery(query);
    while (rs.next())
    {
        page.addTableRow(rs.getString("userName"),
                        rs.getFloat("balance"));
    }
}
catch (SQLException e)
    {}

En condiciones normales, un usuario ingresa su ID de usuario y contraseña, y esto genera la siguiente declaración para su ejecución: 
 

SELECT userName, balance 
FROM accounts 
WHERE userID=512 and password='thisisyoda'

Un posible ataque de inyección SQL explotaría el campo de contraseña para generar una expresión booleana que haría que la expresión se evaluara como verdadera en todos los casos. Imagine establecer los campos de ID de usuario y contraseña como
 

userID = 1' or '1' = '1
password = 1' or '1' = '1

La declaración SQL entonces se convierte en
 

SELECT userName, balance 
FROM accounts 
WHERE userID='1' OR '1'='1' and 
      password='1' OR '1'='1'

La consulta devolverá un valor porque la condición (OR 1=1) siempre es verdadera. De esta forma el sistema ha autenticado al usuario sin conocer el usuario y la contraseña.
La vulnerabilidad se puede mitigar mediante una declaración preparada para crear una consulta parametrizada de la siguiente manera:
 

Java

String query = "SELECT userName, balance "+
               "FROM accounts WHERE userID = ?
                and password = ?";
 
try {
  PreparedStatement statement = connection.prepareStatement(query);
  statement.setInt(1, request.getParameter("userID"));
  ResultSet rs = statement.executeQuery();
  while (rs.next())
  {
    page.addTableRow(rs.getString("userName"),
                     rs.getFloat("balance"));
  }
} catch (SQLException e)
       { ... }

Si un atacante intenta dar un valor al campo ID de usuario que no es un número entero simple, la declaración.setInt() generará un error SQLException en lugar de permitir que se complete la consulta.
Ejemplo 2
considere otro tipo de ataque durante la autenticación: 
 

Java

String query = "SELECT userID, userName, passwordHash"+
               " FROM users WHERE userName = '"
               + request.getParameter("user") + "'";
int userID = -1;
HashMap userGroups = new HashMap();
try
{
  Statement statement = connection.createStatement();
  ResultSet rs = statement.executeQuery(query);
  rs.first();
  userID = rs.getInt("userID");
     
  if (!hashOf(request.getParameter("password")).equals(rs.getString("passwordHash")))
  {
    throw BadLoginException();
  }
 
  String userGroupQuery = "SELECT group FROM groupMembership"+
                         " WHERE userID = " + userID;
     
  rs = statement.executeQuery(userGroupQuery);
     
  while (rs.next())
  {
    userGroup.put(rs.getString("group"), true);
  }
}
catch (SQLException e){}
catch (BadLoginException e){}

Una consulta normal sería la siguiente. 
 

SELECT userID, userName, passwordHash 
FROM users 
WHERE userName = 'Anannya'

El atacante puede inyectar lo siguiente en el campo de nombre de usuario. 
 

Anannya';
INSERT INTO groupMmbership (userID, group) 
VALUES (SELECT userID FROM users
WHERE userName='Anannya', 'Administrator'); --

Debido a esto, la consulta real cambiará a: 
 

SELECT userID, userName, passwordHash FROM 
       users WHERE userName = 'Anannya';
INSERT INTO groupMmbership (userID, group) 
VALUES (SELECT userID FROM users 
WHERE userName='Anannya', 'Administrator'); --'

Esto hará que se agregue otra declaración SQL a la declaración real, lo que hará que el usuario se agregue a la base de datos del administrador. El ataque se puede mitigar mediante el uso de una declaración preparada con una consulta parametrizada de la siguiente manera.
 

Java

String query = "SELECT userID, userName, passwordHash"+
               " FROM users WHERE userName = ?";
 
try
{
  PreparedStatement statement =
         connection.prepareStatement(userLoginQuery);
  statement.setString(1, request.getParameter("user"));
  ResultSet rs = statement.executeQuery();
}

Ejemplo 3
Considere otro ejemplo de vulnerabilidad de consulta discutido a continuación:
 

Java

String query = "INSERT INTO users VALUES(" +
        request.getParameter("userName") + ");";

Una consulta general será: 
 

INSERT INTO users VALUES("Anannya")

Considere si el atacante ingresa la siguiente consulta en el campo de nombre de usuario: 
 

"Anannya); DROP TABLE users;"

La consulta cambiará a: 
 

INSERT INTO users VALUES("Anannya"); DROP TABLE users;

Esta consulta elimina completamente la tabla de usuarios al ejecutarse. Una solución aquí, nuevamente, es una declaración preparada.
¿Cómo ayuda el uso de una declaración preparada en Java?
Una declaración preparada «desinfecta» la entrada. Esto significa que se asegura de que todo lo que ingrese el usuario se trate como una string literal en SQL y NO como parte de la consulta SQL. También puede escapar de ciertos caracteres y detectar/eliminar código malicioso. En otros lenguajes como PHP, filter_input o filter_input_array se pueden usar para desinfectar la string.
Este artículo es una contribución de Anannya Uberoi . Si te gusta GeeksforGeeks y te gustaría contribuir, también puedes escribir un artículo usando write.geeksforgeeks.orgo envíe su artículo por correo a review-team@geeksforgeeks.org. Vea su artículo que aparece en la página principal de GeeksforGeeks y ayude a otros Geeks.
Escriba comentarios si encuentra algo incorrecto o si desea compartir más información sobre el tema tratado anteriormente.
 

Publicación traducida automáticamente

Artículo escrito por GeeksforGeeks-1 y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *