| 屬性 | 值 |
|---|---|
| 規則識別碼 | CA2100 |
| 職稱 | 必須檢閱 SQL 查詢中是否有安全性弱點 |
| 類別 | 安全性 |
| 修正程式是中斷或非中斷 | 不中斷 |
| 在 .NET 10 中預設啟用 | No |
原因
方法會使用從字串自變數建置到 方法的字串來設定 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);
如何修正違規
若要修正此規則的違規,請使用參數化查詢。
隱藏警告的時機
如果命令文字不包含任何使用者輸入,則隱藏此規則的警告是安全的。
隱藏警告
如果您只想要隱藏單一違規,請將預處理器指示詞新增至原始程式檔以停用,然後重新啟用規則。
#pragma warning disable CA2100
// The code that's violating the rule is on this line.
#pragma warning restore CA2100
[*.{cs,vb}]
dotnet_diagnostic.CA2100.severity = none
如需詳細資訊,請參閱 如何隱藏程式代碼分析警告。
設定程式代碼以分析
使用下列選項來設定程式代碼基底要執行此規則的部分。
此外,下列數據流分析相關選項適用於此規則:
- interprocedural_analysis_kind
- max_interprocedural_lambda_or_local_function_call_chain
- max_interprocedural_method_call_chain
- points_to_analysis_kind
- copy_analysis
- sufficient_IterationCount_for_weak_KDF_algorithm
您可以只針對此規則、套用至的所有規則,或套用至此類別的所有規則(安全性)設定這些選項。 如需詳細資訊,請參閱 程式代碼品質規則組態選項。
排除特定符號
您可以藉由設定 [excluded_symbol_names] 選項,從分析中排除特定符號,例如類型和方法。 例如,若要指定規則不應該在名為 MyType的任何程式代碼上執行,請將下列機碼/值組新增至 專案中的 .editorconfig 檔案:
dotnet_code_quality.CAXXXX.excluded_symbol_names = MyType
注意
以適用規則的標識碼取代 XXXX 的 CAXXXX 部分。
選項值中允許的符號名稱格式(以 |分隔):
- 只包含符號名稱(包含名稱的所有符號,不論包含類型或命名空間為何)。
- 符號 檔案識別碼格式的完整名稱。 每個符號名稱都需要符號種類前置詞,例如
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) |
比對特定方法和MyMethod1MyMethod2個別的完整簽章。 |
排除特定類型及其衍生類型
您可以藉由設定 [excluded_type_names_with_derived_types] 選項,從分析中排除特定類型及其衍生類型。 例如,若要指定規則不應該在具名 MyType 類型及其衍生型別內的任何方法上執行,請將下列機碼/值組新增至 專案中的 .editorconfig 檔案:
dotnet_code_quality.CAXXXX.excluded_type_names_with_derived_types = MyType
注意
以適用規則的標識碼取代 XXXX 的 CAXXXX 部分。
選項值中允許的符號名稱格式(以 |分隔):
- 僅限類型名稱(包含名稱的所有類型,不論包含類型或命名空間為何)。
- 符號 文件識別碼格式的完整名稱,具有選擇性
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 |
比對特定類型和MyType1MyType2個別的完整名稱,以及其所有衍生型別。 |
範例
下列範例顯示違反規則的方法 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 資源受控識別。