Inyección de SQL, cómo proteger un programa frente a vulnerabilidades

La inyección SQL es una técnica de inserción de código malicioso a fin de alterar el correcto y normal funcionamiento del programa y que podría destruir su base de datos. La inyección SQL es una de las técnicas de piratería web más comunes. Se basa en incrustar código malicioso en declaraciones SQL, a través de la entrada de una página web.

La inyección de SQL generalmente ocurre durante la ejecución el programa vulnerable, ya sea en local o en la nube, o bien cuando le pide a un usuario que ingrese, como su nombre de usuario o un ID , y en lugar de un nombre o ID, el usuario le da una instrucción SQL que, sin saberlo, ejecutará en su base de datos.

Mire el siguiente ejemplo que crea una instrucción SELECT agregando una variable (txtUserId) a una cadena de selección. La variable se obtiene de la entrada del usuario (getRequestString):

txtUserId = getRequestString("UserId");
txtSQL = "SELECT * FROM Users WHERE UserId = " + txtUserId;

La inyección SQL basada en 1 = 1 es siempre verdadera

Mira el ejemplo anterior de nuevo. El propósito original del código era crear una instrucción SQL para seleccionar un usuario, con una identificación de usuario dada.

Si no hay nada que impida que un usuario ingrese una entrada «incorrecta», el usuario puede ingresar alguna entrada «inteligente» como esta:

UserId: 105 OR 1=1

Entonces, la declaración SQL se verá así:

SELECT columna1, columna2, ...
FROM Usuarios WHERE Usuario_Id = 105 OR 1=1;

El SQL anterior es válido y devolverá TODAS las filas de la tabla «Usuarios», ya que OR 1=1 siempre es VERDADERO.

¿Qué sucede si la tabla «Usuarios» contiene nombres y contraseñas?

La declaración SQL anterior es muy similar a esto:

SELECT Nombre, ID, contraseña
FROM Usuarios WHERE Usuario_Id = 105 OR 1=1;

Un hacker podría obtener acceso a todos los nombres de usuario y contraseñas en una base de datos simplemente insertando 105 O 1=1 en el campo de entrada.

Inyección SQL basada en «»=»» es siempre verdadera

Aquí hay un ejemplo de un inicio de sesión de usuario en un sitio web:

Usuario:

Pedro.martinez

Contraseña:

miContraseña

Ejemplo de SQL Injection

uName = getRequestString("username");
uPass = getRequestString("userpassword");

sql = 'SELECT * FROM Usuarios WHERE Nombre ="' + uName + '" AND Pass ="' + uPass + '"'

Aquí tenemos el resultado de lo anterior

SELECT *
FROM Usuarios WHERE Usuario_Id = "Pedro.martinez" AND Contraseña = "miContraseña"

Un hacker podría obtener acceso a nombres de usuario y contraseñas en una base de datos simplemente insertando » O «»=» en el cuadro de texto de nombre de usuario o contraseña:

Usuario:

» or «»=»

Contraseña

» or «»=»

El código en el servidor creará una instrucción SQL válida como esta:

SELECT * FROM Usuarios WHERE Nombre = "" OR = ""=""  AND Contraseña = "" OR ""=""

Inyección SQL basada en sentencias SQL por Batch

La mayoría de las bases de datos admiten sentencias SQL por batch.

Un batch de sentencias SQL es un grupo de dos o más sentencias SQL, separadas por punto y coma.

La declaración SQL a continuación devolverá todas las filas de la tabla «Usuarios», luego eliminará la tabla «Proveedores».

SELECT *
FROM Usuarios;
DROP TABLE  Proveedores

Ejemplo

txtUserId = getRequestString("IDUsuario");
txtSQL = "SELECT * FROM Usuarios WHERE IDUsuario = " + txtUserId;

Usuario:

105; DROP TABLE Proveedores

La sentencia SQL sería así:

SELECT *
FROM Usuarios WHERE Usuario_Id = 105;  DROP TABLE Proveedores;

Usa estos parámetros de SQL para proteger

Para proteger un sitio web de la inyección SQL, puede usar parámetros SQL.

Los parámetros SQL son valores que se agregan a una consulta SQL en el momento de la ejecución, de manera controlada.

Ejemplo

ASP.NET Razor

txtUserId = getRequestString("IdUsuario");
txtSQL = "SELECT * FROM Usuarios WHERE IdUsuario = @0";
db.Execute(txtSQL,txtUserId);

Tenga en cuenta que los parámetros se representan en la instrucción SQL mediante un marcador @.

El motor SQL verifica cada parámetro para asegurarse de que sea correcto para su columna y se trate literalmente, y no como parte del SQL que se ejecutará.

Ejemplo

txtNam = getRequestString("Nombre_Cliente");
txtAdd = getRequestString("Dirección");
txtCit = getRequestString("Ciudad");
txtSQL = "INSERT INTO Clientes (Nombre_Cliente,Dirección,Ciudad) Values(@0,@1,@2)";
db.Execute(txtSQL,txtNam,txtAdd,txtCit);

Más ejemplos

Los siguientes ejemplos muestran cómo crear consultas parametrizadas en algunos lenguajes web comunes.

Declaración SELECT en ASP.NET:

txtUserId = getRequestString("IdUsuario");
sql = "SELECT * FROM Clientes WHERE Cliente_Id = @0";
command = new SqlCommand(sql);
command.Parameters.AddWithValue("@0",txtUserId);
command.ExecuteReader();

Declaración INSERT INTO en ASP.NET:

txtNam = getRequestString("Nombre_Cliente");
txtAdd = getRequestString("Dirección");
txtCit = getRequestString("Ciudad");
txtSQL = "INSERT INTO Clientes (Nombre_Cliente,Dirección,Ciudad) Values(@0,@1,@2)";
command = new SqlCommand(txtSQL);
command.Parameters.AddWithValue("@0",txtNam);
command.Parameters.AddWithValue("@1",txtAdd);
command.Parameters.AddWithValue("@2",txtCit);
command.ExecuteNonQuery();

Declaración INSERT INTO en PHP:

$stmt = $dbh->prepare("INSERT INTO Clientes (Nombre_Cliente,Dirección,Ciudad)
VALUES (:nam, :add, :cit)");
$stmt->bindParam(':nam', $txtNam);
$stmt->bindParam(':add', $txtAdd);
$stmt->bindParam(':cit', $txtCit);
$stmt->execute();