Inyección de SQL
A pesar de que cada vez se conoce más, la inyección de SQL continúa siendo un problema. Las consecuencias de una inyección exitosa de SQL varían dependiendo de la vulnerabilidad. Algunas de las amenazas que se pueden introducir son estas:
- Revelación de datos
- Modificación de los datos existentes
- Inserción de datos nuevos
- Acceso arbitrario al sistema de archivos
- Acceso arbitrario a la red
- Sistema puesto en riesgo
Cada consulta en la CMA es vulnerable a inyección de SQL, así que usted trabajará varios vectores de entrada. Al inyectar una condición y comentar el resto de la consulta mediante el campo de nombre de usuario, se puede evadir la autenticación. El Listado 16 muestra la consulta como se pretendía.
Listado 16. El argumento SELECT cuando John y Password1 se ingresan como credenciales de usuario
SELECT COUNT(*) FROM UserAccount
WHERE Username = 'John' AND Password = md5('Password1');
|
El Listado 17 muestra cómo se vería el argumento cuando se utiliza una cadena de caracteres maliciosa para inyectar código en la primera condición.
Listado 17. El argumento
select cuando 'or 1=1;# y una contraseña vacía se ingresan como credenciales
SELECT COUNT(*) FROM UserAccount
WHERE Username = ''or 1=1;#' AND Password = md5('');
|
Como 1 siempre es igual a 1 y la condición de verificación de contraseña es comentada por el caracter del signo número (#), la consulta del Listado 17 retorna el conteo de todos los registros en la tabla UserAccount. Si el conteo no es cero, el valor retornado de la función Authenticate se evalúa como verdadero, garantizando acceso al pirata.
La funcionalidad de búsqueda de usuario es vulnerable de una forma que puede explotarse para extraer datos arbitrarios, entre otras cosas. El Listado 18 muestra la consulta de búsqueda como se pretendía que funcionara.
Listado 18. Consulta de búsqueda de usuario bajo condiciones normales
SELECT FirstName, LastName FROM UserAccount WHERE FirstName LIKE '%John%' OR LastName LIKE '%John%'; |
Al utilizar el operador UNION, los atacantes pueden adjuntar una consulta totalmente nueva para capturar los datos que deseen:
'and 1=0 UNION SELECT Username, Password FROM UserAccount;#
El Listado 19 muestra la consulta generada dinámicamente después de presentar una cadena de caracteres de ataque.
Listado 19. La consulta de búsqueda de usuario después de la inyección de SQL
SELECT FirstName, LastName FROM UserAccount WHERE FirstName LIKE '%'and 1=0 UNION SELECT Username, Password FROM UserAccount;#%' OR LastName LIKE '%'and 1=0 UNION SELECT Username, Password FROM UserAccount;#'"; |
El ataque produce un resumen del nombre de usuario y contraseña para cualquier usuario de la base de datos (vea la Figura 5).
Figura 5. Resultado de una inyección exitosa usando el operador UNION
Prevenga las inyecciones de SQL
Para prevenir la inyección de SQL, usted debe escapar y validar adecuadamente todas las entradas presentadas por el usuario. La mayoría de las APIs para desarrollo Web incluyen funciones para lograr esto. Con PHP y MySQL, use consultas parametrizadas junto con mysql_real_escape_string para valores de cadena de caracteres, para protegerse de muchos ataques (vea el Listado 20).
Listado 20. Código de autenticación actualizado utilizando las medidas preventivas ofrecidas por la API PHP
$query = sprintf("SELECT COUNT(*) FROM useraccount " .
"WHERE Username = '%s' AND " .
"Password = md5('%s');",
mysql_real_escape_string($Username),
mysql_real_escape_string($Password));
|
Como muestra el Listado 21 , la evasión de autenticación ya no funciona debido al escape del delimitador al comienzo de la cadena de caracteres de ataque.
Listado 21. Un intento de inyección con la nueva corrección en su lugar
SELECT COUNT(*) FROM useraccount
WHERE Username = '\'or 1=1;#' AND Password = md5('');
|
Es importante usar el especificador de tipo correcto den la cadena de caracteres del formato. Convertir el tipo esperado proporciona una capa de protección adicional (vea el Listado 22).
Listado 22. Creando una consulta de forma segura usando un entero de fuente desconocida
$query = sprintf("SELECT * FROM useraccount WHERE Id = %d", (int)$_GET['id']);
|
La siguiente sección analiza la Inclusión de Archivos, un tipo de error que es común en las aplicaciones Web PHP.