Compartir a través de


Instrucciones de codificación de ADO.NET segura

Para proteger la aplicación es necesario escribir código seguro. El código sólo debe exponer la información y la funcionalidad requeridas por el código de cliente. Entre los ataques más frecuentes relacionados con ADO.NET se encuentran la inserción de SQL y la determinación de información privada de bases de datos a partir de las excepciones devueltas por una aplicación.

Evitar los ataques de inserción de SQL

Se produce una inserción de SQL cuando un atacante inserta instrucciones SQL adicionales en los comandos que se procesan en el origen de datos. Estos comandos pueden recuperar información confidencial, además de modificar o destruir la información del origen de datos. El código vulnerable a la inserción de SQL concatena cadenas de comandos con entradas externas. Por ejemplo, el código siguiente es vulnerable a la inserción de SQL.

' Retrieve CustomerID to search for from external source.
Dim custID As String = GetCustomerID()

' The following line of code allows for SQL Insertion attack.
Dim selectString As String = "SELECT * FROM Customers WHERE CustomerID = " & custID

Dim cmd As SqlCommand = New SqlCommand(selectString, conn)
conn.Open()
Dim myReader As SqlDataReader = cmd.ExecuteReader()
' Process results.
myReader.Close()
conn.Close()
[C#]
// Retrieve CustomerID to search for from external source.
string custID = GetCustomerID();

// The following line of code allows for SQL Insertion attack.
string selectString = "SELECT * FROM Customers WHERE CustomerID = " + custID;

SqlCommand cmd = new SqlCommand(selectString, conn);
conn.Open();
SqlDataReader myReader = cmd.ExecuteReader();
' Process results.
myReader.Close();
conn.Close();

Un atacante podría introducir un valor de "1;DROP TABLE Customers" para consultar el CustomerID. El resultado sería la ejecución del siguiente comando para la consulta.

SELECT * FROM Customers WHERE CustomerID = 1;DROP TABLE Customers

Para protegerse de los ataques de inserción de SQL, valide las entradas de datos de orígenes externos y pase los valores de columna como parámetros en lugar de concatenar valores para crear una instrucción SQL.

Validar la entrada de datos

Se pueden utilizar expresiones regulares para validar que los datos proporcionados coincidan con un formato determinado. .NET Framework proporciona el objeto Regex, que se encarga de validar un valor utilizando una expresión regular. Por ejemplo, el siguiente código garantiza que un valor determinado sea una cadena alfanumérica de 5 caracteres.

Public Static Function Validate(inString As String) As Boolean
  Dim r As Regex = New Regex("^[A-Za-z0-9]{5}$")
  Return r.IsMatch(inString)
End Function
[C#]
public static bool Validate(string inString)
{
  Regex r = new Regex("^[A-Za-z0-9]{5}$");
  return r.IsMatch(inString)
}

Utilizar parámetros

Los parámetros proporcionan un método práctico para organizar los valores que se pasan con una instrucción SQL o los valores que se pasan a un procedimiento almacenado. Además, los parámetros pueden proteger de un ataque de inserción de SQL ya que garantizan que los valores recibidos desde un origen externo se pasen sólo como valores, y no como parte de la instrucción SQL. Como resultado, los comandos SQL insertados en un valor no se ejecutan en el origen de datos. En lugar de ello, los valores que se pasan se consideran simplemente valores de parámetros. El código siguiente muestra un ejemplo de la forma de utilizar un parámetro para pasar un valor.

' Retrieve CustomerID to search for from external source.
Dim custID As String = GetCustomerID()

Dim selectString As String = "SELECT * FROM Customers WHERE CustomerID = @CustomerID"

Dim cmd As SqlCommand = New SqlCommand(selectString, conn)
cmd.Parameters.Add("@CustomerID", SqlDbType.VarChar, 5).Value = custID

conn.Open()
Dim myReader As SqlDataReader = cmd.ExecuteReader()
' Process results.
myReader.Close()
conn.Close()
[C#]
// Retrieve CustomerID to search for from external source.
string custID = GetCustomerID();

string selectString = "SELECT * FROM Customers WHERE CustomerID = @CustomerID";

SqlCommand cmd = new SqlCommand(selectString, conn);
cmd.Parameters.Add("@CustomerID", SqlDbType.VarChar, 5).Value = custID;

conn.Open();
SqlDataReader myReader = cmd.ExecuteReader();
' Process results.
myReader.Close();
conn.Close();

Mantener la confidencialidad de la información de excepciones

Es muy frecuente que los atacantes utilicen la información de una excepción, como el nombre del servidor, de la base de datos o de una tabla, a fin de llevar a cabo un ataque específico contra el sistema. Como las excepciones pueden contener información específica sobre la aplicación o el origen de datos, puede aumentar la protección de ambos exponiendo al cliente solamente la información requerida.

Para evitar la exposición de información confidencial a través de las excepciones, no devuelva el contenido de una excepción del sistema al usuario. En su lugar, controle la excepción internamente. Si es necesario enviar un mensaje al usuario, devuelva su propio mensaje personalizado que contenga la mínima información posible (como "Error en la conexión. Póngase en contacto con el administrador del sistema.") y registre la información específica de forma que un administrador pueda utilizarla.

Por ejemplo, el código siguiente detecta las excepciones al abrir una conexión y escribe la excepción en el registro de eventos.

Dim conn As SqlConnection = New SqlConnection("Data Source=localhost;Initial Catalog=Northwind;")

Try
  conn.Open()

Catch e As SqlException
  Dim log As System.Diagnostics.EventLog = New System.Diagnostics.EventLog()
  log.Source = "My Application"
  log.WriteEntry(e.ToString())

  If conn.State <> ConnectionState.Open Then _
    Console.WriteLine("Connection was not opened.")

Finally
  conn.Close()
End Try
[C#]
SqlConnection conn = new SqlConnection("Data Source=localhost;Initial Catalog=Northwind;");

try
{
  conn.Open();
}
catch (SqlException e)
{
  System.Diagnostics.EventLog log = new System.Diagnostics.EventLog();
  log.Source = "My Application";
  log.WriteEntry(e.ToString());

  if (conn.State != ConnectionState.Open)
    Console.WriteLine("Connection was not opened.");
}
finally
{
  conn.Close();
}

Vea también

Escribir código de ADO.NET seguro