CA3001: Przegląd kodu pod kątem luk w zabezpieczeniach polegających na wstrzyknięciu kodu SQL

Właściwości Wartość
Identyfikator reguły CA3001
Tytuł Przegląd kodu pod kątem luk umożliwiających wstrzyknięcie kodu SQL
Kategoria Bezpieczeństwo
Poprawka łamiąca lub nienaruszająca Niezgodność
Domyślnie włączone na platformie .NET 10 Nie.
Zastosowane języki C# i Visual Basic

Przyczyna

Potencjalnie niezaufane dane wejściowe żądania HTTP docierają do tekstu polecenia SQL.

Domyślnie ta reguła analizuje całą bazę kodu, ale można to skonfigurować.

Opis reguły

Podczas pracy z niezaufanymi danymi wejściowymi i poleceniami SQL należy pamiętać o atakach typu SQL injection. Atak polegający na wstrzyknięciu kodu SQL może wykonywać złośliwe polecenia SQL, co narusza bezpieczeństwo i integralność aplikacji. Typowe techniki obejmują użycie pojedynczego cudzysłowu lub apostrofu do oznaczania ciągów znaków literałów, dwóch kresek dla komentarza i średnika na końcu wyrażenia. Aby uzyskać więcej informacji, zobacz SQL Injection.

Ta reguła próbuje znaleźć dane wejściowe z żądań HTTP docierających do tekstu polecenia SQL.

Uwaga

Ta reguła nie może śledzić danych między modułami. Jeśli na przykład jeden zestaw odczytuje dane wejściowe żądania HTTP, a następnie przekazuje go do innego zestawu, który wykonuje polecenie SQL, ta reguła nie generuje ostrzeżenia.

Uwaga

Istnieje konfigurowalny limit sposobu, w jaki ta reguła będzie analizować przepływ danych między wywołaniami metod. Zobacz Konfiguracja analizatora, aby dowiedzieć się, jak skonfigurować limit w pliku EditorConfig.

Jak naprawić naruszenia

Użyj sparametryzowanych poleceń SQL lub procedur składowanych z parametrami zawierającymi niezaufane dane wejściowe.

Kiedy pomijać ostrzeżenia

Można bezpiecznie pominąć ostrzeżenie z tej reguły, jeśli wiesz, że dane wejściowe są zawsze weryfikowane względem znanego bezpiecznego zestawu znaków.

Pomijanie ostrzeżenia

Jeśli chcesz po prostu pominąć pojedyncze naruszenie, dodaj dyrektywy preprocesora do pliku źródłowego, aby wyłączyć, a następnie ponownie włączyć regułę.

#pragma warning disable CA3001
// The code that's violating the rule is on this line.
#pragma warning restore CA3001

Aby wyłączyć regułę dla pliku, folderu lub projektu, ustaw jego ważność na none w pliku konfiguracji.

[*.{cs,vb}]
dotnet_diagnostic.CA3001.severity = none

Aby uzyskać więcej informacji, zobacz Jak pominąć ostrzeżenia dotyczące analizy kodu.

Konfigurowanie kodu do analizowania

Użyj poniższych opcji, aby skonfigurować, na które części bazy kodu ma być stosowana ta reguła.

Możesz skonfigurować te opcje tylko dla tej reguły, dla wszystkich reguł, do których mają zastosowanie, lub dla wszystkich reguł w tej kategorii (Security), do których mają zastosowanie. Aby uzyskać więcej informacji, zobacz Opcje konfiguracji reguły jakości kodu.

Wyklucz określone symbole

Z analizy można wykluczyć określone symbole, takie jak typy i metody, ustawiając opcję excluded_symbol_names. Aby na przykład określić, że reguła nie powinna być uruchamiana w żadnym kodzie w typach o nazwie MyType, dodaj następującą parę klucz-wartość do pliku editorconfig w projekcie:

dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType

Uwaga

Zastąp część XXXXCAXXXX identyfikatorem odpowiedniej reguły.

Dozwolone formaty nazw symboli w wartości opcji (oddzielone przez |):

  • Tylko nazwa symbolu (zawiera wszystkie symbole o nazwie, niezależnie od typu zawierającego lub przestrzeni nazw).
  • W pełni kwalifikowane nazwy w formacie identyfikatora dokumentacji symbolu. Każda nazwa symbolu wymaga prefiksu określającego rodzaj symbolu, takiego jak M: dla metody, T: dla typów i N: dla przestrzeni nazw.
  • .ctor dla konstruktorów i .cctor dla konstruktorów statycznych.

Przykłady:

Wartość opcji Podsumowanie
dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType Pasuje do wszystkich symboli o nazwie MyType.
dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType1|MyType2 Pasuje do wszystkich symboli o nazwie MyType1 lub MyType2.
dotnet_code_quality.CAXXXX.excluded_symbol_names = M:NS.MyType.MyMethod(ParamType) Dopasowuje określoną metodę MyMethod do określonej, w pełni kwalifikowanej sygnatury.
dotnet_code_quality.CAXXXX.excluded_symbol_names = M:NS1.MyType1.MyMethod1(ParamType)|M:NS2.MyType2.MyMethod2(ParamType) Dopasowuje określone metody MyMethod1 i MyMethod2 z odpowiednimi w pełni kwalifikowanymi sygnaturami.

Wyklucz określone typy i ich pochodne typy

Określone typy i ich typy pochodne można wykluczyć z analizy, ustawiając opcję excluded_type_names_with_derived_types. Aby na przykład określić, że reguła nie powinna być uruchamiana na żadnych metodach w typach nazwanych MyType i ich typach pochodnych, dodaj następującą parę klucz-wartość do pliku .editorconfig w projekcie:

dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = MyType

Uwaga

Zastąp część XXXXCAXXXX identyfikatorem odpowiedniej reguły.

Dozwolone formaty nazw symboli w wartości opcji (oddzielone przez |):

  • Podaj tylko nazwę typu (obejmuje wszystkie typy o tej nazwie, bez względu na typ zawierający lub przestrzeń nazw).
  • W pełni kwalifikowane nazwy w formacie dokumentacyjnego identyfikatora symbolu, z opcjonalnym prefiksem T:.

Przykłady:

Wartość opcji Podsumowanie
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = MyType Pasuje do wszystkich typów nazwanych MyType i wszystkich ich typów pochodnych.
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = MyType1|MyType2 Dopasuje wszystkie typy o nazwie MyType1 lub MyType2 i wszystkie ich typy pochodne.
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = M:NS.MyType Dopasowuje określony typ MyType do danej w pełni kwalifikowanej nazwy i do wszystkich jego pochodnych typów.
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = M:NS1.MyType1|M:NS2.MyType2 Pasuje do określonych typów MyType1 i MyType2 z odpowiednimi w pełni kwalifikowanymi nazwami i wszystkimi ich typami pochodnymi.

Przykłady pseudokodu

Naruszenie

using System;
using System.Data;
using System.Data.SqlClient;

namespace TestNamespace
{
    public partial class WebForm : System.Web.UI.Page
    {
        public static string ConnectionString { get; set; }

        protected void Page_Load(object sender, EventArgs e)
        {
            string name = Request.Form["product_name"];
            using (SqlConnection connection = new SqlConnection(ConnectionString))
            {
                SqlCommand sqlCommand = new SqlCommand()
                {
                    CommandText = "SELECT ProductId FROM Products WHERE ProductName = '" + name + "'",
                    CommandType = CommandType.Text,
                };

                SqlDataReader reader = sqlCommand.ExecuteReader();
            }
        }
    }
}
Imports System
Imports System.Data
Imports System.Data.SqlClient
Imports System.Linq

Namespace VulnerableWebApp
    Partial Public Class WebForm
        Inherits System.Web.UI.Page

        Public Property ConnectionString As String

        Protected Sub Page_Load(sender As Object, e As EventArgs)
            Dim name As String = Me.Request.Form("product_name")
            Using connection As SqlConnection = New SqlConnection(ConnectionString)
                Dim sqlCommand As SqlCommand = New SqlCommand With {.CommandText = "SELECT ProductId FROM Products WHERE ProductName = '" + name + "'",
                                                                    .CommandType = CommandType.Text}
                Dim reader As SqlDataReader = sqlCommand.ExecuteReader()
            End Using
        End Sub
    End Class
End Namespace

Rozwiązanie sparametryzowane

using System;
using System.Data;
using System.Data.SqlClient;

namespace TestNamespace
{
    public partial class WebForm : System.Web.UI.Page
    {
        public static string ConnectionString { get; set; }

        protected void Page_Load(object sender, EventArgs e)
        {
            string name = Request.Form["product_name"];
            using (SqlConnection connection = new SqlConnection(ConnectionString))
            {
                SqlCommand sqlCommand = new SqlCommand()
                {
                    CommandText = "SELECT ProductId FROM Products WHERE ProductName = @productName",
                    CommandType = CommandType.Text,
                };

                sqlCommand.Parameters.Add("@productName", SqlDbType.NVarChar, 128).Value = name;

                SqlDataReader reader = sqlCommand.ExecuteReader();
            }
        }
    }
}
Imports System
Imports System.Data
Imports System.Data.SqlClient
Imports System.Linq

Namespace VulnerableWebApp
    Partial Public Class WebForm
        Inherits System.Web.UI.Page

        Public Property ConnectionString As String

        Protected Sub Page_Load(sender As Object, e As EventArgs)
            Dim name As String = Me.Request.Form("product_name")
            Using connection As SqlConnection = New SqlConnection(ConnectionString)
                Dim sqlCommand As SqlCommand = New SqlCommand With {.CommandText = "SELECT ProductId FROM Products WHERE ProductName = @productName",
                                                                    .CommandType = CommandType.Text}
                sqlCommand.Parameters.Add("@productName", SqlDbType.NVarChar, 128).Value = name
                Dim reader As SqlDataReader = sqlCommand.ExecuteReader()
            End Using
        End Sub
    End Class
End Namespace

Rozwiązanie procedury składowanej

using System;
using System.Data;
using System.Data.SqlClient;

namespace TestNamespace
{
    public partial class WebForm : System.Web.UI.Page
    {
        public static string ConnectionString { get; set; }

        protected void Page_Load(object sender, EventArgs e)
        {
            string name = Request.Form["product_name"];
            using (SqlConnection connection = new SqlConnection(ConnectionString))
            {
                SqlCommand sqlCommand = new SqlCommand()
                {
                    CommandText = "sp_GetProductIdFromName",
                    CommandType = CommandType.StoredProcedure,
                };

                sqlCommand.Parameters.Add("@productName", SqlDbType.NVarChar, 128).Value = name;

                SqlDataReader reader = sqlCommand.ExecuteReader();
            }
        }
    }
}
Imports System
Imports System.Data
Imports System.Data.SqlClient
Imports System.Linq

Namespace VulnerableWebApp
    Partial Public Class WebForm
        Inherits System.Web.UI.Page

        Public Property ConnectionString As String

        Protected Sub Page_Load(sender As Object, e As EventArgs)
            Dim name As String = Me.Request.Form("product_name")
            Using connection As SqlConnection = New SqlConnection(ConnectionString)
                Dim sqlCommand As SqlCommand = New SqlCommand With {.CommandText = "sp_GetProductIdFromName",
                                                                    .CommandType = CommandType.StoredProcedure}
                sqlCommand.Parameters.Add("@productName", SqlDbType.NVarChar, 128).Value = name
                Dim reader As SqlDataReader = sqlCommand.ExecuteReader()
            End Using
        End Sub
    End Class
End Namespace