Seguridad en ASP.NET
Protégete del SQL Injection en ASP.NET

Fecha: 14/Jul/04 (13/07/04)
Autor: Julio César Durán Barragán, juceduba89@hotmail.com


En estos tiempos el ataque por SQL injection es uno de los ataque mas utilizados y por que no decirlo uno de los mas dañinos (“aunque suene tenebroso”) , pero la noticia buena es que llevando a cabo algunas buenas practicas al momento de la programación se pueden evitar estos ataques con un cierto de facilidad si sabes lo que estas haciendo…

Bueno para comenzar vamos a ver un ejemplo donde se ve como en un pagina de autenticación común y corriente la cual pide nombre de usuario y contraseña para validar si la combinación de nombre de usuario y contraseña se encuentran en la tabla de usuarios registrados.

<HTML>

<HEAD>

<script language="vb" runat="server">

Private Sub valida( ByVal sender As System.Object, ByVal e As System.EventArgs) Dim con As New SqlConnection("Server=servidor;DataBase=seguridad;Password=;User ID=sa;") 
    Dim str_com As String = _
    "SELECT COUNT(*) AS Expr1" & _
    " FROM Users" & _
    " WHERE UserName = '" & txt_user.Text & "' AND Password = '" & txt_pass.Text & "'"
    Dim com As New SqlCommand(str_com, con)
    con.Open()
    Dim i As Integer
    Try
        i = com.ExecuteScalar
    Catch ex As SqlException
        lb_mess.Text = ex.Message
    End Try
    con.Close()
    con = Nothing
    If i > 0 Then
        lb_mess.Text = "Bienbenido Ususrio " & txt_user.Text
    Else
        lb_mess.Text = "Nombre de ususario o Contraseña incorrecta"
    End If
End Sub

</script>

</HEAD>

<body MS_POSITIONING="GridLayout">

<form id="Form1" method="post" runat="server">

<TABLE id="Table1" >

<TR>

<TD style="WIDTH: 183px; HEIGHT: 3px">

<asp:Label id="Label1" runat="server" >Nombre de Usuario</asp:Label></TD>

<TD style="HEIGHT: 3px">

<asp:TextBox id="txt_user" runat="server"></asp:TextBox></TD>

<TD style="HEIGHT: 3px"></TD>

</TR>

<TR>

<TD style="WIDTH: 183px">

<asp:Label id="Label2" runat="server">Contraseña</asp:Label></TD>

<TD>

<asp:TextBox id="txt_pass" runat="server"></asp:TextBox></TD>

<TD></TD>

</TR>

<TR>

<TD style="WIDTH: 183px"></TD>

<TD>

<asp:Button id="Button1" OnClick="valida" runat="server" text="Ingresar"></asp:Button></TD>

<TD></TD>

</TR>

</TABLE>

<asp:Label id="lb_mess" runat="server"></asp:Label>

</form>

</body>

</HTML>

El ejemplo anterior usa una conexión a una base de datos llamada seguridad en SQL Server la cual contiene un par de columnas la primera contiene el nombre de usuario y la segunda contiene la contraseña, el usuario ingresa su nombre de usuario y su contraseña y da un clic al botón para validar sus datos, si el usuario es valido aparece un masaje dándole la bienvenida si no aparece un mensaje de error… a por cierto con el siguiente script puedes generar la base de datos que se necesita para este ejemplo…

CREATE DATABASE seguridad

GO

USE seguridad

GO

CREATE TABLE Users (UserName varchar(15) NOT NULL,

Password varchar(10) NOT NULL)

GO

INSERT INTO users (UserName, Password)

VALUES ('user1', 'jikjenf1')

INSERT INTO users (UserName, Password)

VALUES ('user2', 'der256gtr')

GO

Este script lo puedes ejecutar en el analizador de consultas de SQL Server, bueno les decía….. en que estábamos…. A si para validar al usuario , como se dijo antes, se consulta una tabla donde están … guardados los usuarios contamos las veces que se repita el par ingresado , claro solo puede ser 1 o 0 en fin, por medio de la consulta siguiente :

SELECT COUNT(*) AS Expr1

FROM Users

WHERE UserName = 'user2' AND Password = ' der256gtr '

Bueno todo parece muy seguro hasta ahora, pero que pasa si yo ingreso en la caja de texto de nombre de usuario la siguiente línea ..

' or 1 = 1 --

y doy que me autentique, nada, me deja pasar aun y cuando yo no hubiera ingresado una contraseña..

por que pasa esto?, bueno esto pasa por que al ingresar ese renglón en el nombre de usuario la consulta quedaría como sigue a continuación

SELECT COUNT(*) AS Expr1

FROM Users

WHERE UserName = '' or 1 = 1 -- ' AND Password = ' der256gtr '

Se ve algo raro no es cierto, pues si, fijándose un poco pueden observar que con la primera apostrofe se cierra el parámetro de usuario después esta una condición que va hacer verdaderos todos los campos, y enseguida un par de guiones que lo que hacen es comentar todo lo que aparezca después de ellos, entonces esto me regresaría un numero igual al numero de registros en la tabla. Este ejemplo es uno da las cosas mas sencillas que se pueden hacer, imagínense que en ligar de mandar una condición para que me permita entrar mande yo un drop table XXXX estaríamos en graves problemas con los directivos de la empresa ya que este ataque también permite, por medio de errores de consulta obtener el nombre de las tablas e incluso del usuario administrador de la base de datos, claro con un poco de conocimientos en el tema de SQL. Bueno veamos que podríamos hacer para contrarrestar estos ataques en nuestras paginas.

Existen tres formas de protegerse de esto:

La primera seria validar la entrada del usuario por medio de los validadotes que nos proporciona el mismo ASP.NET, donde nada mas permitamos ingresar al usuario los caracteres necesarios para el login y pass quitando la posibilidad de que ingrese algo como “‘,_- etc ..”

Las segunda es usar para las consultas a las bases de datos por medio de Store Procedures. Estos tienen la ventaja que son mas rápidos a la hora de consultar y además están protegidos en contra del SQL Injection

Y la tercera seria no usar un usuario con permiso de administrador para la conexión a la base de datos ya que esto es lo que provoca que los usuarios puedan hacer uso del DROP TABLE

Usando estas tres cosas mencionadas anteriormente nuestra pagina anterior quedaría de la siguiente forma:

<HTML>

<HEAD>

<title>pagina_segura</title>

<script language="vb" runat="server">

Private Sub valida( ByVal sender As System.Object, ByVal e As System.EventArgs) Dim con As New 
    Dim con As New SqlConnection("Server=DESARROLLO01\BYTE;DataBase=seguridad;Password=safeuser;User ID=safeuser;")
    Dim com As New SqlCommand("proc_IsUserValid", con)
    com.CommandType = CommandType.StoredProcedure
    com.Parameters.Add("@UserName", txt_user.Text)
    com.Parameters.Add("@Password", txt_pass.Text)
    con.Open()
    Dim i As Integer
    Try
        i = com.ExecuteScalar
    Catch ex As SqlException
        lb_mess.Text = ex.Message
    End Try
    con.Close()
    con = Nothing
    If i > 0 Then
        lb_mess.Text = "Bienbenido Ususrio " & txt_user.Text
    Else
        lb_mess.Text = "Nombre de ususario o Contraseña incorrecta"
    End If
End Sub

</script>

</HEAD>

<body MS_POSITIONING="GridLayout">

<form id="Form1" method="post" runat="server">

<TABLE id="Table1" style="Z-INDEX: 101; LEFT: 48px; WIDTH: 368px; POSITION: absolute; TOP: 56px; HEIGHT: 104px"

cellSpacing="1" cellPadding="1" width="368" border="1">

<TR>

<TD style="WIDTH: 183px; HEIGHT: 3px">

<asp:Label id="Label1" runat="server" Font-Bold="True" Font-Names="Arial">Nombre de Usuario</asp:Label></TD>

<TD style="WIDTH: 237px; HEIGHT: 3px">

<asp:TextBox id="txt_user" runat="server"></asp:TextBox>

<asp:RequiredFieldValidator id="RequiredFieldValidator1" runat="server" ErrorMessage="Ingreza Nombre de Usuario"

ControlToValidate="txt_user">*</asp:RequiredFieldValidator>

<asp:RegularExpressionValidator id="RegularExpressionValidator1" runat="server" ErrorMessage="RegularExpressionValidator"

ControlToValidate="txt_user" ValidationExpression="[a-zA-Z0-9]*">*</asp:RegularExpressionValidator></TD>

<TD style="HEIGHT: 3px"></TD>

</TR>

<TR>

<TD style="WIDTH: 183px">

<asp:Label id="Label2" runat="server" Font-Bold="True" Font-Names="Arial">Contraseña</asp:Label></TD>

<TD style="WIDTH: 237px">

<asp:TextBox id="txt_pass" runat="server"></asp:TextBox>

<asp:RequiredFieldValidator id="RequiredFieldValidator2" runat="server" ErrorMessage="Ingreza contraseña" ControlToValidate="txt_pass">*</asp:RequiredFieldValidator>

<asp:RegularExpressionValidator id="RegularExpressionValidator2" runat="server" ErrorMessage="RegularExpressionValidator"

ControlToValidate="txt_pass" ValidationExpression="[a-zA-Z0-9]*">*</asp:RegularExpressionValidator></TD>

<TD></TD>

</TR>

<TR>

<TD style="WIDTH: 183px"></TD>

<TD style="WIDTH: 237px">

<asp:Button id="Button1" runat="server" Text="Ingresar"></asp:Button></TD>

<TD>&nbsp;&nbsp;</TD>

</TR>

</TABLE>

<asp:Label id="lb_mess" style="Z-INDEX: 102; LEFT: 67px; POSITION: absolute; TOP: 188px" runat="server"></asp:Label>

<asp:ValidationSummary id="ValidationSummary1" style="Z-INDEX: 103; LEFT: 443px; POSITION: absolute; TOP: 65px"

runat="server"></asp:ValidationSummary></form>

</body>

</HTML>

Este ultimo ejemplo incorpora las tres fases que habíamos mencionado con anterioridad, estamos verificando que en la entrada del usuario solo vengan letras o números además de que estamos requiriendo los dos campos esto es para que el servidor realice una operación que sabemos que va a regresar falso y para no hacer trabajar innecesariamente al servidor, estamos usando un store procedure para realizar la petición a la base de datos y ademada estamos usando un usuario con privilegios mínimos para realizar la consulta.

A continuación te pongo el script que se necesita para crear la tabla, ingresa dos registros a la BD y además crea el store procedure y el usuario con privilegios mínimos que usamos en el ejemplo anterior

CREATE DATABASE seguridad

GO

USE seguridad

GO

CREATE TABLE Users (UserName varchar(15) NOT NULL,

Password varchar(10) NOT NULL)

GO

INSERT INTO users (UserName, Password)

VALUES ('user1', 'jikjenf1')

INSERT INTO users (UserName, Password)

VALUES ('user2', 'der256gtr')

GO

CREATE PROCEDURE proc_IsUserValid

@UserName VarChar (32), @Password VarChar (32) AS

SELECT COUNT (*) FROM Users WHERE UserName = @UserName AND

CAST (Password AS VarBinary) = CAST (@Password AS VarBinary)

GO

EXEC sp_addlogin 'SafeUser', 'safeuser', 'seguridad'

GO

EXEC sp_grantdbaccess 'SafeUser'

GO

GRANT EXECUTE ON proc_IsUserValid TO SafeUser

GO

Si tienes algún comentario acerca de este articulo no dudes en comunicarte con un servidor arriba esta mi correo.

Si te gusto o te sirvió el articulo por favor vótalo en la parte de arriba Gracias



ir al índice

Fichero con el código de ejemplo: juceduba_sqlinjection.zip- 4 KB