CA3001: Revisión de código en busca de vulnerabilidades de inyección de SQL

Propiedad Value
Identificador de la regla CA3001
Título Revisión de código en busca de vulnerabilidades de inyección de SQL
Categoría Seguridad
La corrección interrumpe o no interrumpe Poco problemático
Habilitado de forma predeterminada en .NET 8 No

Causa

Una entrada de solicitud HTTP que puede no ser de confianza llega al texto de un comando SQL.

De forma predeterminada, esta regla analiza todo el código base, pero esto es configurable.

Descripción de la regla

Al trabajar con comandos SQL y de entrada que no son de confianza, tenga en cuentan los ataques por inyección de código SQL. Un ataque por inyección de código SQL puede ejecutar comandos SQL malintencionados y poner en peligro la seguridad y la integridad de la aplicación. Las técnicas típicas incluyen usar una comilla simple o un apóstrofo para delimitar cadenas literales, dos guiones para un comentario y un punto y coma para el final de una instrucción. Para obtener más información, consulte SQL Injection.

Esta regla intenta buscar la entrada de las solicitudes HTTP que llega al texto de un comando SQL.

Nota

Esta regla no puede realizar el seguimiento de los datos entre ensamblados. Por ejemplo, si un ensamblado lee la entrada de la solicitud HTTP y, a continuación, la pasa a otro ensamblado que ejecuta el comando SQL, esta regla no generará ninguna advertencia.

Nota

Existe un límite configurable para el nivel de profundidad con que esta regla analizará el flujo de datos a través de las llamadas a métodos. Vea Configuración del analizador para saber cómo configurar el límite en un archivo EditorConfig.

Cómo corregir infracciones

Use comandos SQL con parámetros o procedimientos almacenados con parámetros que contengan la entrada que no sea de confianza.

Cuándo suprimir las advertencias

Es seguro suprimir una advertencia de esta regla si sabe que la entrada siempre se valida con respecto a un conjunto de caracteres seguro conocido.

Supresión de una advertencia

Si solo quiere suprimir una única infracción, agregue directivas de preprocesador al archivo de origen para deshabilitar y volver a habilitar la regla.

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

Para deshabilitar la regla de un archivo, una carpeta o un proyecto, establezca su gravedad en none del archivo de configuración.

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

Para obtener más información, consulte Procedimiento para suprimir advertencias de análisis de código.

Configuración del código para analizar

Use las opciones siguientes para configurar en qué partes del código base se va a ejecutar esta regla.

Puede configurar estas opciones solo para esta regla, para todas las reglas a las que se aplica o para todas las reglas de esta categoría (Seguridad) a las que se aplica. Para más información, vea Opciones de configuración de reglas de calidad de código.

Exclusión de símbolos específicos

Puede excluir símbolos específicos, como tipos y métodos, del análisis. Por ejemplo, para especificar que la regla no se debe ejecutar en ningún código dentro de los tipos con el nombre MyType, agregue el siguiente par clave-valor a un archivo .editorconfig en el proyecto:

dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType

Formatos de nombre de símbolo permitidos en el valor de opción (separados por |):

  • Solo nombre de símbolo (incluye todos los símbolos con el nombre, con independencia del tipo contenedor o el espacio de nombres).
  • Nombres completos en el formato de id. de documentación del símbolo. Cada nombre de símbolo necesita un prefijo de tipo símbolo, como M: para los métodos, T: para los tipos y N: para los espacios de nombres.
  • .ctor para los constructores y .cctor para los constructores estáticos.

Ejemplos:

Valor de la opción Resumen
dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType Coincide con todos los símbolos denominados MyType.
dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType1|MyType2 Coincide con todos los símbolos denominados MyType1 o MyType2.
dotnet_code_quality.CAXXXX.excluded_symbol_names = M:NS.MyType.MyMethod(ParamType) Coincide con un método MyMethod concreto con la signatura completa especificada.
dotnet_code_quality.CAXXXX.excluded_symbol_names = M:NS1.MyType1.MyMethod1(ParamType)|M:NS2.MyType2.MyMethod2(ParamType) Coincide con los métodos MyMethod1 y MyMethod2 concretos con las signaturas completas especificadas.

Exclusión de tipos específicos y sus tipos derivados

Puede excluir tipos específicos y sus tipos derivados del análisis. Por ejemplo, para especificar que la regla no se debe ejecutar en ningún método dentro de los tipos con el nombre MyType y sus tipos derivados, agregue el siguiente par clave-valor a un archivo .editorconfig en el proyecto:

dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = MyType

Formatos de nombre de símbolo permitidos en el valor de opción (separados por |):

  • Solo nombre de tipo (incluye todos los tipos con el nombre, con independencia del tipo contenedor o el espacio de nombres).
  • Nombres completos en el formato de identificador de documentación del símbolo, con un prefijo T: opcional.

Ejemplos:

Valor de la opción Resumen
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = MyType Coincide con todos los tipos denominados MyType y todos sus tipos derivados.
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = MyType1|MyType2 Coincide con todos los tipos denominados MyType1 o MyType2, y todos sus tipos derivados.
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = M:NS.MyType Coincide con un tipo MyType específico con el nombre completo dado y todos sus tipos derivados.
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = M:NS1.MyType1|M:NS2.MyType2 Coincide con los tipos MyType1 y MyType2 específicos con los correspondientes nombres completos y todos sus tipos derivados.

Ejemplos de pseudocódigo

Infracción

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

Solución con parámetros

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

Solución de procedimiento almacenado

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