CA2100: проанализируйте SQL-запросы с целью выявления уязвимостей безопасности
Свойство | Значение |
---|---|
Идентификатор правила | CA2100 |
Заголовок | Проверьте запросы SQL на наличие уязвимостей системы безопасности |
Категория | Безопасность |
Исправление является критическим или не критическим | Не критическое |
Включен по умолчанию в .NET 9 | No |
Причина
Метод задает System.Data.IDbCommand.CommandText свойство с помощью строки, созданной из строкового аргумента в метод.
По умолчанию это правило анализирует всю базу кода, но такое поведение можно настроить.
Описание правила
Это правило предполагает, что любая строка, значение которой невозможно определить во время компиляции, может содержать входные данные пользователя. Командная строка SQL, созданная из ввода пользователем, уязвима для атак внедрения SQL. При атаке путем внедрения кода SQL злоумышленник предоставляет входные данные, которые изменяют структуру запроса при попытке нанести вред или получить несанкционированный доступ к основной базе данных. Типичные методы включают в себя введение одинарной кавычки или апострофа, который является разделителем строк для литерала SQL; двух тире, обозначающих комментарий SQL; и точки с запятой, которая указывает на новую команду. Если входные данные пользователя должны быть частью запроса, используйте один из следующих методов, перечисленных в порядке эффективности, чтобы снизить риск атаки.
- Использование хранимой процедуры.
- Использование параметризованной командной строки.
- Проверка данных, введенных пользователем, на предмет типа и содержимого перед сборкой командной строки.
Следующие типы .NET реализуют свойство CommandText или предоставляют конструкторы, которые устанавливают свойство с помощью строкового аргумента.
- System.Data.Odbc.OdbcCommand и System.Data.Odbc.OdbcDataAdapter
- System.Data.OleDb.OleDbCommand и System.Data.OleDb.OleDbDataAdapter
- System.Data.OracleClient.OracleCommand и System.Data.OracleClient.OracleDataAdapter
- System.Data.SqlClient.SqlCommand и System.Data.SqlClient.SqlDataAdapter
В некоторых случаях это правило может не определять значение строки во время компиляции, даже если это возможно. В этих случаях это правило создает ложноположительный результат при использовании этих строк в качестве команд SQL. Ниже приводится пример подобной строки.
int x = 10;
string query = "SELECT TOP " + x.ToString() + " FROM Table";
То же самое применяется при неявном использовании ToString()
.
int x = 10;
string query = String.Format("SELECT TOP {0} FROM Table", x);
Устранение нарушений
Чтобы устранить нарушение этого правила, используйте параметризованный запрос.
Когда лучше отключить предупреждения
Вы можете отключить предупреждение этого правила, если текст команды не содержит данных, введенных пользователем.
Отключение предупреждений
Если вы просто хотите отключить одно нарушение, добавьте директивы препроцессора в исходный файл, чтобы отключить и повторно включить правило.
#pragma warning disable CA2100
// The code that's violating the rule is on this line.
#pragma warning restore CA2100
Чтобы отключить правило для файла, папки или проекта, задайте его серьезность none
в файле конфигурации.
[*.{cs,vb}]
dotnet_diagnostic.CA2100.severity = none
Дополнительные сведения см. в разделе Практическое руководство. Скрытие предупреждений анализа кода.
Настройка кода для анализа
Используйте следующие параметры, чтобы указать части базы кода, к которым будет применяться это правило.
Эти параметры можно настроить только для этого правила, для всех правил, к которым она применяется, или для всех правил в этой категории (безопасности), к которым она применяется. Дополнительные сведения см. в статье Параметры конфигурации правила качества кода.
Исключение определенных символов
Вы можете исключить из анализа определенные символы, например типы и методы. Например, чтобы указать, что правило не должно выполняться для какого-либо кода в типах с именем MyType
, добавьте следующую пару "ключ-значение" в файл EDITORCONFIG в своем проекте:
dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType
Заметка
Замените XXXX
частью CAXXXX
идентификатором применимого правила.
Допустимые форматы имени символа в значении параметра (разделенные |
):
- Только имя символа (включает все символы с этим именем, любого типа и в любом пространстве имен).
- Полные имена в формате идентификатора документации для символа. Для каждого имени символа требуется префикс в виде символа, например
M:
для методов,T:
для типов иN:
для пространств имен. -
.ctor
используется для конструкторов, а.cctor
— для статических конструкторов.
Примеры:
Значение параметра | Итоги |
---|---|
dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType |
Соответствует всем символам с именем MyType . |
dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType1|MyType2 |
Соответствует всем символам с именем MyType1 или MyType2 . |
dotnet_code_quality.CAXXXX.excluded_symbol_names = M:NS.MyType.MyMethod(ParamType) |
Соответствует конкретному методу MyMethod с заданной полной сигнатурой. |
dotnet_code_quality.CAXXXX.excluded_symbol_names = M:NS1.MyType1.MyMethod1(ParamType)|M:NS2.MyType2.MyMethod2(ParamType) |
Соответствует конкретным методам MyMethod1 и MyMethod2 с соответствующими полными сигнатурами. |
Исключить определенные типы и их производные типы
Из анализа можно исключать определенные типы и их производные типы. Например, чтобы указать, что правило не должно выполняться в каких-либо методах типов MyType
и их производных типов, добавьте следующую пару "ключ-значение" в файл .editorconfig своего проекта:
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = MyType
Заметка
Замените XXXX
частью CAXXXX
идентификатором применимого правила.
Допустимые форматы имени символа в значении параметра (разделенные |
):
- Только имя типа (включает все типы с этим именем, любого типа и в любом пространстве имен).
- полные имена в формате идентификатора документации для символа с необязательным префиксом
T:
.
Примеры:
Значение параметра | Итоги |
---|---|
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = MyType |
Соответствует всем типам с именем MyType и всем их производным типам. |
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = MyType1|MyType2 |
Соответствует всем типам с именем MyType1 или MyType2 и всем их производным типам. |
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = M:NS.MyType |
Соответствует конкретному типу MyType с заданным полным именем и всем производным от него типам. |
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = M:NS1.MyType1|M:NS2.MyType2 |
Соответствует конкретным типам MyType1 и MyType2 с заданным полным именем и всем производным от них типам. |
Пример
В следующем примере показан метод, UnsafeQuery
который нарушает правило. В нем также показан метод, SaferQuery
который удовлетворяет правилу с помощью параметризованной командной строки.
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]'
}
}
Внимание
Корпорация Майкрософт рекомендует использовать самый безопасный поток проверки подлинности. Если вы подключаетесь к SQL Azure, управляемые удостоверения для ресурсов Azure — это рекомендуемый метод проверки подлинности.