Scour Design. Tutoriales de Flash, ActionScript, CSS, Photoshop, ImageReady...

\n"; } } } lecturas();

          


ADO.NET Evitar el SQL Injection (Visual C#)

      

Autor: Carlos Carmona

Para empezar, una pequeña explicación sobre qué es el SQL Injection.

SQL Injection
Se podría definir como la alteración mal intencionada de una orden SQL a base de datos por parte de un usuario, con el objetivo de obtener, insertar, modificar y/o borrar datos sin la autorización del propietario.

Un ejemplo sencillo:

Tenemos una página que crea dinámicamente un enlace con una variable, 'id' que envía por get:

  1. DivContenido.InnerHtlm = "<a href=\"receptor.aspx?id=" + id + "\">Click Aquí</a>";

Y en dicha página (receptor.aspx), tomamos directamente dicha variable y formamos la sentencia SQL concatenando la variable a la sentencia:

  1. private void Page_Load(object sender, System.EventArgs e)
  2. {
  3. string id = "";
  4. id = Request.QueryString["id"].ToString();
  5. string conexion = "Data Source=localhost;User id=sa;Password=sa;Initial
    Catalog=agenda"
    ;
  6. SqlConnection con = new SqlConnection (conexion);
  7. SqlDataAdapter da = new SqlDataAdapter("select * from contactos where id=" + id,
    con);
  8. DataSet ds = new DataSet();
  9. da.Fill(ds);
  10. DataGrid1.DataSource = ds;
  11. DataGrid1.DataBind();
  12. }

Esto permitiría que un usuario con conocimientos de SQL, modificase los valores de la variable id por get, poniendo, por ejemplo:

  1. http://www.url.com/receptor.aspx?id=1%20union%20drop%20table%20tabla

Lo que escribiría:

  1. SELECT * FROM contactos WHERE id = 1 union DROP TABLE tabla

Es decir, este usuario nos borraría la tabla de nombre 'tabla'.

Igual alguien podría pensar que el problema del SQL Injection solo se presenta cuando el usuario puede interactuar con los valores de las sentencias a través del navegador, ya sea por variables vía get o un TextBox (input type="text") , en absoluto, aunque el usuario no tenga acceso a las variables a través del navegador, hay programas que envían paquetes definidos por el usuario a través de diversos puertos, en el caso de la web, el puerto 80, luego, siempre que vayamos a hacer una transacción a base de datos que contenga variables, deberemos hacerlo mediante los parámetros implementados en .NET, que evitan si o si la inyección de SQL por parte de los usuarios.

El modo correcto de realizar la anterior sentencia sería:

  1. private void Page_Load(object sender, System.EventArgs e)
  2. {
  3. string id = "";
  4. id = Request.QueryString["id"].ToString();
  5. string conexion = "Data Source=localhost;User id=sa;Password=sa;Initial
    Catalog=agenda"
    ;
  6. SqlConnection con = new SqlConnection (conexion);
  7. SqlDataAdapter da = new SqlDataAdapter("select * from contactos where id = @id",
    con);
  8. SqlParameter param1 = new SqlParameter("@id", SqlDbType.Int, 4);
  9. param1.Direction = ParameterDirection.Input;
  10. param1.Value = id;
  11. da.SelectCommand.Parameters.Add(param1);
  12. DataSet ds = new DataSet();
  13. da.Fill(ds);
  14. DataGrid1.DataSource = ds;
  15. DataGrid1.DataBind();
  16. }

También podemos escribir la forma abreviada de los parametros:

  1. da.SelectCommand.Parameters.Add("@id", SqlDbType.Int, 4).Value = id;

El concepto de Direction especifica si el parametro es de entrada o salida, realmente solo es necesario cuando el comando hace una llamada a un procedimiento almacenado de Transact-SQL donde hay declarada una variable como OutPut, en cuyo caso, así deberemos reflejarlo en el parametro, dentro de Direction.

La estructura básica de los parámetros es:

  1. Comando.Parameters.Add(string variable, SqlDbType Tipo_dato, longitud).Value = valor;

Si un tipo de dato de un campo en base de datos está especificado como Numeric (Decimal) y en el parametro decimos que es un int, en principio funcionaría, pero nos arriesgamos a provocar un desbordamiento, por lo que lo recomendable es especificar el tipo de dato que consta en base de datos.

En el ejemplo anterior he usado un SqlDataAdapter, ya que se trataba de traer datos, pero para un insert, update o delete con SqlCommand sería igual:

  1. private void Page_Load(object sender, System.EventArgs e)
  2. {
  3. string id = ident.Text;
  4. string conexion = "Data Source=localhost;User id=sa;Password=sa;Initial
    Catalog=agenda"
    ;
  5. SqlConnection con = new SqlConnection (conexion);
  6. SqlCommand cmd = new SqlCommand("delete from cartera where ident = @id", con);
  7. /* **************** MODO CORTO **************** */
  8. cmd.Parameters.Add("@id", SqlDbType.Int, 4).Value = id;
  9. /* ******************************************** */
  10. /* **************** MODO LARGO **************** */
  11. SqlParameter param1 = new SqlParameter("@id", SqlDbType.Int, 4);
  12. param1.Direction = ParameterDirection.Input;
  13. param1.Value = id;
  14. cmd.Parameters.Add(param1);
  15. /* ******************************************** */
  16. try
  17. {
  18. cmd.ExecuteNonQuery();
  19. }
  20. catch (Exception errorMens)
  21. {
  22. error.Visible = true;
  23. error.Text = errorMens.Message;
  24. }
  25. finally
  26. {
  27. cmd.Connection.Close();
  28. }
  29. }

Espacio de Nombres requerido:

  1. using System.Data.SqlClient;

Para más información, consulte la referencia del MSDN acerca de parametros en SQL bajo .NET.

Nota
En un foro (no recuerdo donde), me han dado un pequeño tirón de orejas (merecido) en referencia a que la sentencia "union" no permite usar deletes, tal y como yo especifiqué en el ejemplo... muy cierto (ya no se me vuelve a olvidar). En cualquier caso si que podría hacer (para ese ejemplo en concreto) un select y obtener datos confidenciales de clientes.
Scour Design ™ Todos los Derechos Reservados © Carlos Carmona Xhtml 1.1 Strict Válido!CSS Nivel 2 Válido! Nivel Triple-A de Conformidad con las Directrices de Accesibilidad Web (WAI)