CA2100: revisar consultas SQL para vulnerabilidades de segurança
TypeName |
ReviewSqlQueriesForSecurityVulnerabilities |
CheckId |
CA2100 |
Categoria |
Microsoft.Security |
Alteração Significativa |
Sem quebra |
Causa
Um método define a propriedade de IDbCommand.CommandText usando uma cadeia de caracteres que é criada a partir de um argumento de cadeia de caracteres para o método.
Descrição da Regra
Esta regra presume que o argumento de cadeia de caracteres contém a entrada do usuário.Uma cadeia de caracteres de comando SQL que é criada da entrada do usuário é vulnerável a ataques de injeção SQL.Em um ataque de injeção SQL, um usuário mal-intencionado fornece entrada que modificar o design de uma consulta na tentativa de danificar ou ter acesso não autorizado a base de dados subjacente.As técnicas básicas incluem a injeção de uma aspa simples ou de apóstrofo, que é o delimitador de cadeia de caracteres literal SQL; dois características, que significa um comentário SQL; e um ponto-e-vírgula, que indica que um novo comando a seguir.Se a entrada de usuário deve fazer parte da consulta, use uma das opções a seguir, listados em ordem de eficiência, para reduzir o risco de ataque.
Use um procedimento armazenado.
Use uma cadeia de caracteres de comando com parâmetros.
Validar entrada do usuário para o tipo e o conteúdo antes de compilar a cadeia de caracteres de comando.
Os seguintes tipos de .NET Framework implementam a propriedade de CommandText ou fornecem construtores esse conjunto a propriedade usando um argumento de cadeia de caracteres.
Observe que esta regra for violada quando o método ToString de um tipo é usado para construir explicitamente ou implicitamente a cadeia de caracteres da consulta.A seguir temos um exemplo.
int x = 10;
string query = "SELECT TOP " + x.ToString() + " FROM Table";
A regra é violada como um usuário mal-intencionado pode substituir o método de ToString() .
A regra é violada também quando ToString é usado implicitamente.
int x = 10;
string query = String.Format("SELECT TOP {0} FROM Table", x);
Como Corrigir Violações
Para corrigir uma violação desta regra, use uma consulta parametrizada.
Quando Suprimir Alertas
É seguro suprimir um aviso dessa regra se o texto do comando não contém nenhuma entrada do usuário.
Exemplo
O exemplo a seguir mostra um método, UnsafeQuery, que viola a regra e um método, SaferQuery, que obedece à regra usando uma cadeia de caracteres de comando com parâmetros.
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'
}