CA2100:SQL クエリのセキュリティ脆弱性を確認
プロパティ | 値 |
---|---|
ルール ID | CA2100 |
Title | SQL クエリのセキュリティ脆弱性を確認 |
[カテゴリ] | Security |
修正が中断か中断なしであるか | なし |
.NET 8 では既定で有効 | いいえ |
原因
メソッドに渡された文字列引数から構築された文字列を使用して System.Data.IDbCommand.CommandText プロパティが設定されています。
既定で、このルールではコードベース全体を分析しますが、これは構成可能です。
規則の説明
この規則では、コンパイル時に値を決定できない文字列は、ユーザー入力を含む可能性があることを想定としています。 ユーザー入力から構築された SQL コマンド文字列には、SQL 注入攻撃に対する脆弱性があります。 SQL インジェクション攻撃では、悪意のあるユーザーが、基盤となるデータベースに障害を発生させたり、不正アクセスを試みたりするために、クエリのデザインを変更する入力を行います。 一般的な手法としては、単一引用符またはアポストロフィ (SQL リテラル文字列の区切り記号)、2 つのダッシュ (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
オプションの値で使用できるシンボル名の形式 (|
で区切ります):
- シンボル名のみ (包含する型または名前空間に関係なく、その名前が指定されたすべてのシンボルが含まれます)。
- そのシンボルのドキュメント 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]'
}
}
関連項目
.NET