Review data adapters vulnerable to SQL injection
Matches methods creating a DB adapter (like SqlDataAdapter) that call Fill() without using DB command parameters: prone to SQL Injection https://en.wikipedia.org/wiki/SQL_injection since malicious SQL might be injected in string parameter values.
False positives are possible; review matches and suppress issues when needed. To limit them, the rule also checks command parameters access from sub method calls (a solid indication of non-vulnerability).
Flagged Code
False positives are possible; review matches and suppress issues when needed. To limit them, the rule also checks command parameters access from sub method calls (a solid indication of non-vulnerability).
Flagged Code
public DataTable LoadOrders(SqlConnection connection, string customerId) {
var sql = "SELECT * FROM Orders WHERE CustomerId = '" + customerId + "'";
using var adapter = new SqlDataAdapter(sql, connection); // flagged: no command parameter
var table = new DataTable();
adapter.Fill(table);
return table;
}
Fixed Codepublic DataTable LoadOrders(SqlConnection connection, string customerId) {
const string sql = "SELECT * FROM Orders WHERE CustomerId = @customerId";
using var adapter = new SqlDataAdapter(sql, connection);
adapter.SelectCommand.Parameters.AddWithValue("@customerId", customerId); // parameterized
var table = new DataTable();
adapter.Fill(table);
return table;
}How to fix
If the method indeed injects parameter values in the SQL string, use adapter.SelectCommand.Parameters.Add(...) instead (or adapter.UpdateCommand / adapter.InsertCommand, depending on context). More on explicit parameters: https://stackoverflow.com/questions/4892166/how-does-sqlparameter-prevent-sql-injection
CQLinq source
This rule is written in CQLinq, C# LINQ over the NDepend code model. It ships with every new NDepend project: you can read it, adapt it, or use it as a starting point for your own custom rules.
// <Name>Review data adapters vulnerable to SQL injection</Name>
// <Id>ND3108:ReviewDataAdaptersVulnerableToSQLInjection</Id>
warnif count > 0
let adapters = ThirdParty.Types.WithFullNameIn(
"System.Data.Common.DataAdapter",
"System.Data.Common.DbDataAdapter",
"Microsoft.Data.SqlClient.SqlDataAdapter",
"System.Data.SqlClient.SqlDataAdapter",
"System.Data.OleDb.OleDbDataAdapter",
"System.Data.Odbc.OdbcDataAdapter",
"System.Data.OracleClient.OracleDataAdapter")
where adapters.Any()
let adapterCtors = adapters.ChildMethods().Where(m => m.IsConstructor).ToHashSetEx()
let adapterFill = adapters.ChildMethods().Where(m => m.SimpleName.Contains("Fill")).ToHashSetEx()
let commands = ThirdParty.Types.WithFullNameIn(
"System.Data.Common.DbCommand",
"Microsoft.Data.SqlClient.SqlCommand",
"System.Data.SqlClient.SqlCommand",
"System.Data.OleDb.OleDbCommand",
"System.Data.Odbc.OdbcCommand",
"System.Data.OracleClient.OracleCommand")
where commands.Any()
let commandParameters = commands.ChildMethods().Where(m => m.IsPropertyGetter && m.SimpleNameEquals("get_Parameters")).ToHashSetEx()
from m in Application.Methods.UsingAny(adapterCtors)
.UsingAny(adapterFill)
where !m.MethodsCalled.Intersect(commandParameters).Any() &&
// Check also that non of the method call rely on command parameters to get less false positives:
!m.MethodsCalled.SelectMany(mc => mc.MethodsCalled).Intersect(commandParameters).Any()
select new { m,
createA = m.MethodsCalled.Intersect(adapterCtors).First().ParentType,
calls = m.MethodsCalled.Intersect(adapterFill).First(),
Debt = 15.ToMinutes().ToDebt(),
Severity = Severity.High
}
//<Expl>
// The *{0}* creates an instance of *{1}* and calls the *{2}*.
// This is susceptible to *SQL Injection* as malicious SQL code
// can be injected into string parameter values.
// Instead, use *adapter.SelectCommand.Parameters.Add(...)*.
//</Expl>
// <Desc>
// Matches methods creating a DB adapter (like *SqlDataAdapter*) that call
// *Fill()* without using DB command parameters: prone to **SQL Injection**
// https://en.wikipedia.org/wiki/SQL_injection since malicious SQL might be
// injected in string parameter values.
//
// False positives are possible; review matches and suppress issues when
// needed. To limit them, the rule also checks command parameters access
// from sub method calls (a solid indication of non-vulnerability).
//
// **Flagged Code**
// ```csharp
// public DataTable LoadOrders(SqlConnection connection, string customerId) {
// var sql = "SELECT * FROM Orders WHERE CustomerId = '" + customerId + "'";
// using var adapter = new SqlDataAdapter(sql, connection); // flagged: no command parameter
// var table = new DataTable();
// adapter.Fill(table);
// return table;
// }
// ```
//
// **Fixed Code**
// ```csharp
// public DataTable LoadOrders(SqlConnection connection, string customerId) {
// const string sql = "SELECT * FROM Orders WHERE CustomerId = @customerId";
// using var adapter = new SqlDataAdapter(sql, connection);
// adapter.SelectCommand.Parameters.AddWithValue("@customerId", customerId); // parameterized
// var table = new DataTable();
// adapter.Fill(table);
// return table;
// }
// ```
// </Desc>
// <HowToFix>
// If the method indeed injects parameter values in the SQL string, use
// **adapter.SelectCommand.Parameters.Add(...)** instead (or
// *adapter.UpdateCommand* / *adapter.InsertCommand*, depending on context).
// More on explicit parameters:
// https://stackoverflow.com/questions/4892166/how-does-sqlparameter-prevent-sql-injection
// </HowToFix>
Open in the interactive Rules Explorer · All default rules
Security: all 39 items
- Software Composition Analysis (SCA) ND3120
- Transitive Software Composition Analysis (SCA) ND3121
- Don't hard-code secrets in constants ND3114
- Review credentials built from hard-coded values ND3115
- Don't use insecure deserializers ND3110
- Don't use weak hash algorithms (MD5, SHA1) ND3109
- Don't use DES/3DES/RC2 weak cipher algorithms ND3102
- Don't use weak password-based key derivation ND3123
- Don't use the ECB encryption mode ND3132
- Don't disable certificate validation ND3103
- Don't hard-code TLS/SSL protocol versions ND3122
- Review commands vulnerable to SQL injection ND3107
- Review data adapters vulnerable to SQL injection ND3108
- Review raw SQL built from composed strings ND3128
- Review LDAP filters composed from strings ND3137
- Don't enable external XML entity resolution (XXE) ND3111
- Review processes started with a composed arguments string ND3124
- Review web endpoints building file paths from their parameters ND3129
- Review custom archive extraction (Zip Slip) ND3135
- Review assemblies and types loaded from runtime-provided names ND3125
- Review web endpoints without [Authorize] nor [AllowAnonymous] ND3118
- Review state-changing endpoints opened to anonymous callers ND3130
- Review state-changing MVC actions without anti-forgery validation ND3119
- Review HTML-encoding bypasses ND3133
- Review explicitly disabled request validation ND3134
- Review redirects to a URL that is not validated as local ND3126
- Review request-influenced outbound HTTP calls (SSRF) ND3136
- Review insecure ASP.NET Core CORS policies and unguarded developer exception page ND3116
- Review cookies created without HttpOnly and Secure flags ND3117
- Review regular expressions without a match timeout (ReDoS) ND3112
- Review secrets compared with non-constant-time equality ND3113
- Review secrets passed to logging methods ND3127
- Review security operations that log nothing ND3131
- Don't use System.Random for security purposes ND3101
- Don't use CoSetProxyBlanket and CoInitializeSecurity ND3100
- Review publicly visible event handlers ND3104
- Pointers should not be publicly visible ND3105
- Seal methods that satisfy non-public interfaces ND3106
- Restrict sensitive APIs to reviewed components ND3138