CA2100: Revisar las consultas SQL en busca de vulnerabilidades de seguridad
Nombre de tipo |
ReviewSqlQueriesForSecurityVulnerabilities |
Identificador de comprobación |
CA2100 |
Categoría |
Microsoft.Security |
Cambio problemático |
Poco problemático |
Motivo
Un método establece la propiedad IDbCommand.CommandText utilizando una cadena que se compila partiendo de un argumento de cadena al método.
Descripción de la regla
Esta regla supone que el argumento de cadena contiene datos proporcionados por el usuario.Una cadena de comandos de SQL compilada a partir de datos proporcionados por el usuario es vulnerable a ataques de inserción de SQL.En un ataque de inserción de SQL, un usuario malintencionado proporciona datos que alteran el diseño de una consulta, a fin de intentar deteriorar la base de datos subyacente u obtener acceso a ella.Entre las técnicas más usadas se encuentra la inserción de una sola comilla o un solo apóstrofe, que es el delimitador de cadena literal de SQL; dos guiones, que significan un comentario de SQL; o un signo de punto y coma, que indica que lo que sigue es un nuevo comando.Si es imprescindible que el usuario introduzca datos en la consulta, utilice uno de los siguientes métodos, que se enumeran por orden de eficacia, para reducir el riesgo de ataques.
Utilice un procedimiento almacenado.
Utilice una cadena de comandos parametrizada.
Valide el tipo y el contenido de los datos proporcionados por el usuario antes de compilar la cadena de comandos.
Los tipos de .NET Framework siguientes implementan la propiedad CommandText o proporcionan constructores que establecen la propiedad mediante un argumento de cadena.
System.Data.Odbc.OdbcCommand y System.Data.Odbc.OdbcDataAdapter
System.Data.OleDb.OleDbCommand y System.Data.OleDb.OleDbDataAdapter
System.Data.OracleClient.OracleCommand y System.Data.OracleClient.OracleDataAdapter
[System.Data.SqlServerCe.SqlCeCommand] y [System.Data.SqlServerCe.SqlCeDataAdapter]
System.Data.SqlClient.SqlCommand y System.Data.SqlClient.SqlDataAdapter
Observe que esta regla se infringe si el método ToString de un tipo se utiliza explícita o implícitamente para construir la cadena de consulta.A continuación, se muestra un ejemplo.
int x = 10;
string query = "SELECT TOP " + x.ToString() + " FROM Table";
La regla se infringe porque un usuario malintencionado puede invalidar el método ToString().
La regla también se infringe si ToString se utiliza implícitamente.
int x = 10;
string query = String.Format("SELECT TOP {0} FROM Table", x);
Cómo corregir infracciones
Para corregir una infracción de esta regla, utilice una consulta parametrizada.
Cuándo suprimir advertencias
Es seguro suprimir una advertencia de esta regla si el texto del comando no contiene ningún dato proporcionado por el usuario.
Ejemplo
En el ejemplo siguiente se muestra un método, UnsafeQuery, que infringe la regla y otro, SaferQuery, que la cumple utilizando una cadena de comandos parametrizada.
Imports System
Imports System.Data
Imports System.Data.SqlClient
Namespace SecurityLibrary
Public Class SqlQueries
Function UnsafeQuery(connection As String, _
name As String, password As String) As Object
Dim someConnection As New SqlConnection(connection)
Dim someCommand As New SqlCommand()
someCommand.Connection = someConnection
someCommand.CommandText = "SELECT AccountNumber FROM Users " & _
"WHERE Username='" & name & "' AND Password='" & password & "'"
someConnection.Open()
Dim accountNumber As Object = someCommand.ExecuteScalar()
someConnection.Close()
Return accountNumber
End Function
Function SaferQuery(connection As String, _
name As String, password As String) As Object
Dim someConnection As New SqlConnection(connection)
Dim someCommand As New SqlCommand()
someCommand.Connection = someConnection
someCommand.Parameters.Add( _
"@username", SqlDbType.NChar).Value = name
someCommand.Parameters.Add( _
"@password", SqlDbType.NChar).Value = password
someCommand.CommandText = "SELECT AccountNumber FROM Users " & _
"WHERE Username=@username AND Password=@password"
someConnection.Open()
Dim accountNumber As Object = someCommand.ExecuteScalar()
someConnection.Close()
Return accountNumber
End Function
End Class
Class MalaciousCode
Shared Sub Main(args As String())
Dim queries As New SqlQueries()
queries.UnsafeQuery(args(0), "' OR 1=1 --", "anything")
' Resultant query (which is always true):
' SELECT AccountNumber FROM Users WHERE Username='' OR 1=1
queries.SaferQuery(args(0), "' OR 1 = 1 --", "anything")
' Resultant query (notice the additional single quote character):
' SELECT AccountNumber FROM Users WHERE Username=''' OR 1=1 --'
' AND Password='anything'
End Sub
End Class
End Namespace
using System;
using System.Data;
using System.Data.SqlClient;
namespace SecurityLibrary
{
public class SqlQueries
{
public object UnsafeQuery(
string connection, string name, string password)
{
SqlConnection someConnection = new SqlConnection(connection);
SqlCommand someCommand = new SqlCommand();
someCommand.Connection = someConnection;
someCommand.CommandText = "SELECT AccountNumber FROM Users " +
"WHERE Username='" + name +
"' AND Password='" + password + "'";
someConnection.Open();
object accountNumber = someCommand.ExecuteScalar();
someConnection.Close();
return accountNumber;
}
public object SaferQuery(
string connection, string name, string password)
{
SqlConnection someConnection = new SqlConnection(connection);
SqlCommand someCommand = new SqlCommand();
someCommand.Connection = someConnection;
someCommand.Parameters.Add(
"@username", SqlDbType.NChar).Value = name;
someCommand.Parameters.Add(
"@password", SqlDbType.NChar).Value = password;
someCommand.CommandText = "SELECT AccountNumber FROM Users " +
"WHERE Username=@username AND Password=@password";
someConnection.Open();
object accountNumber = someCommand.ExecuteScalar();
someConnection.Close();
return accountNumber;
}
}
class MalaciousCode
{
static void Main(string[] args)
{
SqlQueries queries = new SqlQueries();
queries.UnsafeQuery(args[0], "' OR 1=1 --", "anything");
// Resultant query (which is always true):
// SELECT AccountNumber FROM Users WHERE Username='' OR 1=1
queries.SaferQuery(args[0], "' OR 1 = 1 --", "anything");
// Resultant query (notice the additional single quote character):
// SELECT AccountNumber FROM Users WHERE Username=''' OR 1=1 --'
// AND Password='anything'
}
}
}
#using <System.dll>
#using <System.Data.dll>
#using <System.EnterpriseServices.dll>
#using <System.Transactions.dll>
#using <System.Xml.dll>
using namespace System;
using namespace System::Data;
using namespace System::Data::SqlClient;
namespace SecurityLibrary
{
public ref class SqlQueries
{
public:
Object^ UnsafeQuery(
String^ connection, String^ name, String^ password)
{
SqlConnection^ someConnection = gcnew SqlConnection(connection);
SqlCommand^ someCommand = gcnew SqlCommand();
someCommand->Connection = someConnection;
someCommand->CommandText = String::Concat(
"SELECT AccountNumber FROM Users WHERE Username='",
name, "' AND Password='", password, "'");
someConnection->Open();
Object^ accountNumber = someCommand->ExecuteScalar();
someConnection->Close();
return accountNumber;
}
Object^ SaferQuery(
String^ connection, String^ name, String^ password)
{
SqlConnection^ someConnection = gcnew SqlConnection(connection);
SqlCommand^ someCommand = gcnew SqlCommand();
someCommand->Connection = someConnection;
someCommand->Parameters->Add(
"@username", SqlDbType::NChar)->Value = name;
someCommand->Parameters->Add(
"@password", SqlDbType::NChar)->Value = password;
someCommand->CommandText = "SELECT AccountNumber FROM Users "
"WHERE Username=@username AND Password=@password";
someConnection->Open();
Object^ accountNumber = someCommand->ExecuteScalar();
someConnection->Close();
return accountNumber;
}
};
}
using namespace SecurityLibrary;
void main()
{
SqlQueries^ queries = gcnew SqlQueries();
queries->UnsafeQuery(Environment::GetCommandLineArgs()[1],
"' OR 1=1 --", "anything");
// Resultant query (which is always true):
// SELECT AccountNumber FROM Users WHERE Username='' OR 1=1
queries->SaferQuery(Environment::GetCommandLineArgs()[1],
"' OR 1 = 1 --", "anything");
// Resultant query (notice the additional single quote character):
// SELECT AccountNumber FROM Users WHERE Username=''' OR 1=1 --'
// AND Password='anything'
}