CA2100: Revisar consultas SQL em busca de vulnerabilidades de segurança
Property | valor |
---|---|
ID da regra | CA2100 |
Título | Revise consultas SQL em busca de vulnerabilidades de segurança |
Categoria | Segurança |
A correção está quebrando ou não quebrando | Sem quebra |
Habilitado por padrão no .NET 9 | Não |
Motivo
Um método define a System.Data.IDbCommand.CommandText propriedade usando uma cadeia de caracteres que é criada a partir de um argumento de cadeia de caracteres para o método.
Por padrão, essa regra analisa toda a base de código, mas isso é configurável.
Descrição da regra
Esta regra pressupõe que qualquer cadeia de caracteres cujo valor não possa ser determinado em tempo de compilação pode conter entrada do usuário. Uma cadeia de caracteres de comando SQL criada a partir da entrada do usuário é vulnerável a ataques de injeção de SQL. Em um ataque de injeção de SQL, um usuário mal-intencionado fornece entrada que altera o design de uma consulta na tentativa de danificar ou obter acesso não autorizado ao banco de dados subjacente. As técnicas típicas incluem a injeção de uma única aspa ou apóstrofo, que é o delimitador de cadeia de caracteres literal SQL; dois traços, que significa um comentário SQL; e um ponto-e-vírgula, que indica que um novo comando se segue. Se a entrada do usuário precisar fazer parte da consulta, use uma das opções a seguir, listadas em ordem de eficácia, para reduzir o risco de ataque.
- Use um procedimento armazenado.
- Use uma cadeia de caracteres de comando parametrizada.
- Valide a entrada do usuário para o tipo e o conteúdo antes de criar a cadeia de comando.
Os seguintes tipos .NET implementam a CommandText propriedade ou fornecem construtores que definem a propriedade usando um argumento de cadeia de caracteres.
- System.Data.Odbc.OdbcCommand e System.Data.Odbc.OdbcDataAdapter
- System.Data.OleDb.OleDbCommand e System.Data.OleDb.OleDbDataAdapter
- System.Data.OracleClient.OracleCommand e System.Data.OracleClient.OracleDataAdapter
- System.Data.SqlClient.SqlCommand e System.Data.SqlClient.SqlDataAdapter
Em alguns casos, essa regra pode não determinar o valor de uma cadeia de caracteres em tempo de compilação, mesmo que você possa. Nesses casos, essa regra produz falsos positivos ao usar essas cadeias de caracteres como comandos SQL. A seguir está um exemplo de tal cadeia de caracteres.
int x = 10;
string query = "SELECT TOP " + x.ToString() + " FROM Table";
O mesmo se aplica quando se utiliza ToString()
implicitamente.
int x = 10;
string query = String.Format("SELECT TOP {0} FROM Table", x);
Como corrigir violações
Para corrigir uma violação dessa regra, use uma consulta parametrizada.
Quando suprimir avisos
É seguro suprimir um aviso dessa regra se o texto do comando não contiver nenhuma entrada do usuário.
Suprimir um aviso
Se você quiser apenas suprimir uma única violação, adicione diretivas de pré-processador ao seu arquivo de origem para desativar e, em seguida, reativar a regra.
#pragma warning disable CA2100
// The code that's violating the rule is on this line.
#pragma warning restore CA2100
Para desabilitar a regra de um arquivo, pasta ou projeto, defina sua gravidade como none
no arquivo de configuração.
[*.{cs,vb}]
dotnet_diagnostic.CA2100.severity = none
Para obter mais informações, consulte Como suprimir avisos de análise de código.
Configurar código para análise
Use as opções a seguir para configurar em quais partes da base de código executar essa regra.
Você pode configurar essas opções apenas para esta regra, para todas as regras às quais ela se aplica ou para todas as regras nesta categoria (Segurança) às quais ela se aplica. Para obter mais informações, consulte Opções de configuração da regra de qualidade de código.
Excluir símbolos específicos
Você pode excluir símbolos específicos, como tipos e métodos, da análise. Por exemplo, para especificar que a regra não deve ser executada em nenhum código dentro de tipos nomeados MyType
, adicione o seguinte par chave-valor a um arquivo .editorconfig em seu projeto:
dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType
Formatos de nome de símbolo permitidos no valor da opção (separados por |
):
- Somente nome do símbolo (inclui todos os símbolos com o nome, independentemente do tipo ou namespace que o contém).
- Nomes totalmente qualificados no formato de ID de documentação do símbolo. Cada nome de símbolo requer um prefixo de tipo de símbolo, como
M:
para métodos,T:
para tipos eN:
para namespaces. .ctor
para construtores e.cctor
para construtores estáticos.
Exemplos:
Valor da opção | Resumo |
---|---|
dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType |
Corresponde a todos os símbolos denominados MyType . |
dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType1|MyType2 |
Corresponde a todos os símbolos denominados ou MyType1 MyType2 . |
dotnet_code_quality.CAXXXX.excluded_symbol_names = M:NS.MyType.MyMethod(ParamType) |
Corresponde ao método MyMethod específico com a assinatura totalmente qualificada especificada. |
dotnet_code_quality.CAXXXX.excluded_symbol_names = M:NS1.MyType1.MyMethod1(ParamType)|M:NS2.MyType2.MyMethod2(ParamType) |
Corresponde a métodos MyMethod1 específicos e MyMethod2 com as respetivas assinaturas totalmente qualificadas. |
Excluir tipos específicos e seus tipos derivados
Você pode excluir tipos específicos e seus tipos derivados da análise. Por exemplo, para especificar que a regra não deve ser executada em nenhum método dentro de tipos nomeados MyType
e seus tipos derivados, adicione o seguinte par chave-valor a um arquivo .editorconfig em seu projeto:
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = MyType
Formatos de nome de símbolo permitidos no valor da opção (separados por |
):
- Somente nome do tipo (inclui todos os tipos com o nome, independentemente do tipo ou namespace que o contém).
- Nomes totalmente qualificados no formato de ID de documentação do símbolo, com um prefixo opcional
T:
.
Exemplos:
Valor da opção | Resumo |
---|---|
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = MyType |
Corresponde a todos os tipos nomeados MyType e todos os seus tipos derivados. |
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = MyType1|MyType2 |
Corresponde a todos os tipos nomeados ou MyType1 e MyType2 todos os seus tipos derivados. |
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = M:NS.MyType |
Corresponde a um tipo MyType específico com um determinado nome totalmente qualificado e todos os seus tipos derivados. |
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = M:NS1.MyType1|M:NS2.MyType2 |
Corresponde a tipos MyType1 específicos e MyType2 com os respetivos nomes totalmente qualificados, e todos os seus tipos derivados. |
Exemplo
O exemplo a seguir mostra um método, UnsafeQuery
, que viola a regra. Ele também mostra um método, SaferQuery
, que satisfaz a regra usando uma cadeia de comando parametrizada.
Imports System
Imports System.Data
Imports System.Data.SqlClient
Namespace ca2100
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 MaliciousCode
Shared Sub Main2100(args As String())
Dim queries As New SqlQueries()
queries.UnsafeQuery(args(0), "' OR 1=1 --", "[PLACEHOLDER]")
' Resultant query (which is always true):
' SELECT AccountNumber FROM Users WHERE Username='' OR 1=1
queries.SaferQuery(args(0), "' OR 1=1 --", "[PLACEHOLDER]")
' Resultant query (notice the additional single quote character):
' SELECT AccountNumber FROM Users WHERE Username=''' OR 1=1 --'
' AND Password='[PLACEHOLDER]'
End Sub
End Class
End Namespace
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 MaliciousCode
{
static void Main2100(string[] args)
{
SqlQueries queries = new SqlQueries();
queries.UnsafeQuery(args[0], "' OR 1=1 --", "[PLACEHOLDER]");
// Resultant query (which is always true):
// SELECT AccountNumber FROM Users WHERE Username='' OR 1=1
queries.SaferQuery(args[0], "' OR 1=1 --", "[PLACEHOLDER]");
// Resultant query (notice the additional single quote character):
// SELECT AccountNumber FROM Users WHERE Username=''' OR 1=1 --'
// AND Password='[PLACEHOLDER]'
}
}
Importante
A Microsoft recomenda que você use o fluxo de autenticação mais seguro disponível. Se você estiver se conectando ao SQL do Azure, as Identidades Gerenciadas para recursos do Azure serão o método de autenticação recomendado.