SQL
Este tipo de errores puede permitir a usuarios malintencionados acceder a datos
a los que de otro modo no tendrían acceso y, en el peor de los casos,
modificar el comportamiento de nuestras aplicaciones.

Vamos a ver con un ejemplo que significa eso
de “Inyección de código”:

Supongamos que tenemos una aplicación Web (realizada en ASP por sencillez)
en la que el acceso a ciertas secciones está restringido. Para restringir
ese acceso creamos una tabla de usuarios y contraseñas y sólo
los usuarios que se validen contra esa tabla podrán acceder a esos contenidos.
Una manera de que los usuarios se validen será colocar un par de cuadros
de texto en nuestra página Web (por ejemplo txtUsuario y txtPassword)
donde puedan introducir su nombre y su contraseña y enviar ese par usuario/contraseña
a la base de datos para comprobar si es válido.

Primero creamos la tabla que vamos a usar
y la rellenamos con datos:

use web
-- Nuestra base de datos se llama web
go
-- Creamos una tabla para almacenar los pares usuario/contraseña
create table usuarios (Usuario varchar (50) not null primary key, Password varchar (50))
go
-- Introducimos un par de datos de prueba
insert into usuarios (Usuario, Password) values ('Admin', '1234')
insert into usuarios (Usuario, Password) values ('Usuario', 'abcd')

Ahora veamos el código de las páginas
que forman parte del proceso de login.

index.htm
<form action="login.asp" method="post">
Nombre: <input type="text" name="txtUsuario"><br>
Password: <input type="password" name="txtPassword"><br>
<input type="submit">
</form>

Esta primera página es sencilla. Simplemente
los dos cuadros de texto mencionados que enviarán los datos a la página de login.

Login.asp
<%
Dim Usuario, Password, RS, SSQL
Usuario = Request.Form("txtUsuario")
Password = Request.Form("txtPassword")
SSQL = "SELECT count(*) FROM Usuarios WHERE Usuario = '" & Usuario &
"' AND password='" & Password & "'"
Set RS = Server.CreateObject("ADODB.Recordset")
RS.Open SSQL, "Cadena de conexion"
If (RS.EOF) Then
Response.Write "Acceso denegado."
Else
Response.Write "Te has identificado como " & RS("Usuario")
End If
Set RS = Nothing
%>

Y en esta segunda página creamos dinámicamente
una sentencia SQL que enviamos a la base de datos para la validación.

Si el usuario escribe Admin y 1234 la sentencia creada será:

“SELECT Count(*) FROM Usuarios WHERE Usuario=’Admin’ AND 
Password=’1234’”

Y como esta sentencia nos devuelve un registro,
dejaremos que el usuario entre en la Web. Si el usuario escribe por ejemplo
‘Admin’ y de contraseña cualquier otra cosa, la sentencia no nos devolverá registros
y no permitiremos entrar a esa persona.

Pero ¿qué ocurre si el usuario
escribe ‘ or ‘1’=’1 como usuario y lo mismo de contraseña?

En este caso la variable Consulta contendrá
la cadena:

"SELECT Count(*) FROM Usuarios WHERE Usuario = '' or '1'='1' AND
password = '' or '1'='1'"

Y obviamente esta sentencia nos devuelve registros
con lo que el usuario entrará en nuestra Web sin tener permiso.

Pero esto no es lo peor. Lo peor será
que el usuario utilice estos trucos de inyección de SQL para ejecutar
código arbitrario en nuestro servidor. Sentencias DDL, cambiar permisos,
utilizar procedimientos almacenados y un largo etcétera. Qué ocurriría
si alguien escribiese de contraseña cosas como:

' exec master..xp_cmdshell 'net user test /ADD' –

Como evitarlo:

Y ahora lo más importante, ¿qué
podemos hacer para evitar estos errores?
Pues hay varios sistemas para evitarlo. Por ejemplo podemos filtrar las entradas
de los usuarios reemplazando la aparición de ‘ por ‘’
(dos comillas simples) e incluso evitando que los usuarios puedan pasar caracteres
como \ / “ ‘ o cualquier otro que se nos ocurra que puede causar
problemas. Estos filtros pueden ser tan sencillos como utilizar la sentencia
replace de Visual Basic:

SSQL= "SELECT count(*) FROM Usuarios WHERE Usuario = '" & Replace
txtUsuario.Text, "'", "''") & "' AND password='" & Replace
(txtPassword.Text, "'", "''") & "'"

Otro factor importante en cuanto a la seguridad
es limitar al máximo los permisos del usuario que ejecuta estas sentencias
para evitar posibles problemas. Por ejemplo utilizando un usuario distinto para
las sentencias SELECT, DELETE, UPDATE y asegurándonos que cada ejecución
de una sentencia ejecute una sentencia del tipo permitido.

Por supuesto utilizar el usuario ‘sa’
o uno que pertenezca al rol ‘db_owner’ para ejecutar las sentencias
de uso habitual de la base de datos debería quedar descartado.

Una solución definitiva sería trabajar con procedimientos almacenados.
El modo en el que se pasan los parámetros a los procedimientos almacenados
evita que la inyección SQL pueda ser usada. Por ejemplo utilizando el
siguiente procedimiento almacenado:

CREATE Procedure Validar @usuario varchar(50), @password varchar(50)
AS
If (SELECT Count(*) FROM Usuarios WHERE Usuario=@Usuario and Password=@password)>0
Return 1
Return 0

También deberíamos validar los
datos que introduce el usuario teniendo en cuenta por ejemplo la longitud de
los campos y el tipo de datos aceptados. Esto lo podemos hacer en el cliente
con los RegularExpressionValidator o con los CustomValidators del VB.NET. De
todos modos si la seguridad es importante todas estas validaciones hay que repetirlas
en el servidor.

Por ultimo, y ya que estamos pensando en entornos
Web, podemos programar en ASP.NET y utilizar siempre que sea posible las clases
System.Web.Security.FormsAuthentication para que los usuarios entren en nuestras
aplicaciones Web.