次の方法で共有


CA2100:SQL クエリのセキュリティ脆弱性を確認

プロパティ
ルール ID CA2100
Title SQL クエリのセキュリティ脆弱性を確認
[カテゴリ] Security
修正が中断か中断なしであるか なし
.NET 10 で既定で有効 いいえ

原因

メソッドは、文字列引数からメソッドに構築された文字列を使用して、 System.Data.IDbCommand.CommandText プロパティを設定します。

既定で、このルールではコードベース全体を分析しますが、これは構成可能です。

規則の説明

この規則では、コンパイル時に値を決定できない文字列にユーザー入力が含まれている可能性があることを前提としています。 ユーザー入力から構築された SQL コマンド文字列は、SQL インジェクション攻撃に対して脆弱です。 SQL インジェクション攻撃では、悪意のあるユーザーが、基盤となるデータベースに障害を発生させたり、不正アクセスを試みたりするために、クエリのデザインを変更する入力を行います。 一般的な手法としては、単一引用符またはアポストロフィ (SQL リテラル文字列の区切り記号)、2 つのダッシュ (SQL コメントであることを示します)、セミコロン (新しいコマンドが続くことを示します) の挿入があります。 ユーザー入力をクエリの一部にする必要がある場合は、次のいずれかの方法を使用して攻撃のリスクを軽減します (効果の高い順に示しています)。

  • ストアド プロシージャを使用します。
  • パラメーター化されたコマンド文字列を使用します。
  • コマンド文字列を作成する前に、型とコンテンツの両方についてユーザー入力を検証します。

次の .NET 型は、 CommandText プロパティを実装するか、文字列引数を使用してプロパティを設定するコンストラクターを提供します。

場合によっては、可能な場合でも、この規則によってコンパイル時に文字列の値が決定されないことがあります。 そのような場合、この規則では、これらの文字列を 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

詳細については、「コード分析の警告を抑制する方法」を参照してください。

分析するコードを構成する

次のオプションを使用して、コードベースのどの部分に対してこのルールを実行するかを構成します。

さらに、このルールには、次のデータフロー分析関連のオプションが適用されます。

これらのオプションは、このルールのみ、適用されるすべてのルール、または適用先のこのカテゴリ (セキュリティ) のすべてのルールに対して構成できます。 詳細については、「コード品質規則の構成オプション」を参照してください。

特定のシンボルを除外する

excluded_symbol_names オプションを設定することで、種類やメソッドなどの特定のシンボルを分析から除外できます。 たとえば、MyType という名前の型のコードで規則を実行しないように指定するには、プロジェクトの .editorconfig ファイルに次のキーと値のペアを追加します。

dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType

メモ

XXXXCAXXXX 部分を該当するルールの ID に置き換えます。

オプションの値で使用できるシンボル名の形式 (| で区切ります):

  • シンボル名のみ (包含する型または名前空間に関係なく、その名前が指定されたすべてのシンボルが含まれます)。
  • そのシンボルのドキュメント 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 を検索します。

特定の型とその派生型を除外する

excluded_type_names_with_derived_types オプションを設定することで、特定の型とその派生型を分析から除外できます。 たとえば、MyType という名前の型のメソッドとその派生型で規則を実行しないように指定するには、プロジェクトの .editorconfig ファイルに次のキーと値のペアを追加します。

dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = MyType

メモ

XXXXCAXXXX 部分を該当するルールの ID に置き換えます。

オプションの値で使用できるシンボル名の形式 (| で区切ります):

  • 型の名前のみ (包含する型または名前空間に関係なく、その名前が指定されたすべての型が含まれます)。
  • そのシンボルのドキュメント 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.Data
Imports System.Data.OleDb
Imports System.Runtime.Versioning

Namespace ca2100

    Public Class SqlQueries

        <SupportedOSPlatform("windows")>
        Function UnsafeQuery(connection As String,
         name As String, password As String) As Object

            Dim someConnection As New OleDbConnection(connection)
            Dim someCommand As New OleDbCommand With {
                .Connection = someConnection,
                .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

        <SupportedOSPlatform("windows")>
        Function SaferQuery(connection As String,
         name As String, password As String) As Object

            Dim someConnection As New OleDbConnection(connection)
            Dim someCommand As New OleDbCommand With {
                .Connection = someConnection
            }

            someCommand.Parameters.AddWithValue(
            "@username", OleDbType.Char).Value = name
            someCommand.Parameters.AddWithValue(
            "@password", OleDbType.Char).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

        <SupportedOSPlatform("windows")>
        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
[SupportedOSPlatform("Windows")]
public class OleDbQueries
{
    public object UnsafeQuery(
       string connection, string name, string password)
    {
        using OleDbConnection someConnection = new(connection);
        using OleDbCommand someCommand = new();
        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)
    {
        using OleDbConnection someConnection = new(connection);
        using OleDbCommand someCommand = new();
        someCommand.Connection = someConnection;

        someCommand.Parameters.Add(
           "@username", OleDbType.Char).Value = name;
        someCommand.Parameters.Add(
           "@password", OleDbType.Char).Value = password;
        someCommand.CommandText = "SELECT AccountNumber FROM Users " +
           "WHERE Username=@username AND Password=@password";

        someConnection.Open();
        object accountNumber = someCommand.ExecuteScalar();
        someConnection.Close();
        return accountNumber;
    }
}

[SupportedOSPlatform("Windows")]
class MaliciousCode
{
    static void Main2100(string[] args)
    {
        OleDbQueries queries = new();
        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]'
    }
}

重要

Microsoft では、使用可能な最も安全な認証フローを使用することをお勧めします。 Azure SQL に接続する場合は、Azure リソースの管理 ID が推奨される認証方法です。

関連項目