CA2100:检查 SQL 查询是否存在安全漏洞

2022-02-20 19:42:28 浏览数 (1)

规则 ID

CA2100

类别

安全性

修复是中断修复还是非中断修复

非中断

原因

一种方法使用按该方法的字符串参数生成的字符串设置 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);

如何解决冲突

若要解决此规则的冲突,请使用参数化查询。

何时禁止显示警告

如果命令文本不包含任何用户输入,可禁止显示此规则的警告。

配置代码以进行分析

使用下面的选项来配置代码库的哪些部分要运行此规则。

排除特定符号

排除特定类型及其派生类型

你可以仅为此规则、为所有规则或为此类别(安全性)中的所有规则配置这些选项。 有关详细信息,请参阅代码质量规则配置选项。

排除特定符号

可以从分析中排除特定符号,如类型和方法。 例如,若要指定规则不应针对名为 MyType 的类型中的任何代码运行,请将以下键值对添加到项目中的 .editorconfig 文件:

dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType

选项值中允许的符号名称格式(用 | 分隔):

仅符号名称(包括具有相应名称的所有符号,不考虑包含的类型或命名空间)。

完全限定的名称,使用符号的文档 ID 格式。 每个符号名称都需要带有一个符号类型前缀,例如表示方法的 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

选项值中允许的符号名称格式(用 | 分隔):

仅类型名称(包括具有相应名称的所有类型,不考虑包含的类型或命名空间)。

完全限定的名称,使用符号的文档 ID 格式,前缀为 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]'

}

}

另请参阅

安全性概述

0 人点赞