← back to index

S2077 — SQL queries should not be dynamically formatted

Language: C#  |  Type: VULNERABILITY  |  Severity: Major

Tags: cwe, bad-practice, sql, former-hotspot

Dynamically building SQL query strings can result in broken SQL syntax and open SQL injection attacks.

Why is this an issue?

When SQL queries are constructed by concatenating or formatting user-supplied values directly into the query string, the structure of the query itself can be altered by a malicious input. This rule flags calls to SQL execution functions where the query string is built using string concatenation or format operators rather than parameterized queries or prepared statements. Unlike rule {rule:csharpsquid:S3649}, this rule does not perform taint analysis — it flags all dynamically formatted SQL queries as a potential risk regardless of the data source.

What is the potential impact?

SQL injection

If any part of a dynamically formatted query string originates from untrusted input, an attacker can manipulate the query to read, modify, or delete data they should not have access to, bypass authentication checks, or in some configurations execute operating system commands.

How to fix it

Code examples

The following code builds a SQL query by concatenating a value directly into the query string.

Noncompliant code example

public void Foo(DbContext context, string query, string param)
{
    string sensitiveQuery = string.Concat(query, param);
    context.Database.ExecuteSqlCommand(sensitiveQuery); // Noncompliant
    context.Query<User>().FromSql(sensitiveQuery); // Noncompliant

    context.Database.ExecuteSqlCommand($"SELECT * FROM mytable WHERE mycol={value}", param); // Noncompliant: the FormattableString is evaluated and converted to RawSqlString
    string query = $"SELECT * FROM mytable WHERE mycol={param}";
    context.Database.ExecuteSqlCommand(query); // Noncompliant: the FormattableString has already been evaluated, it won't be converted to a parametrized query.
}

public void Bar(SqlConnection connection, string param)
{
    SqlCommand command;
    string sensitiveQuery = string.Format("INSERT INTO Users (name) VALUES (\"{0}\")", param);
    command = new SqlCommand(sensitiveQuery); // Noncompliant

    command.CommandText = sensitiveQuery; // Noncompliant

    SqlDataAdapter adapter;
    adapter = new SqlDataAdapter(sensitiveQuery, connection); // Noncompliant
}

Compliant solution

public void Foo(DbContext context, string query, string param)
{
    context.Database.ExecuteSqlCommand("SELECT * FROM mytable WHERE mycol=@p0", param); // Compliant, it's a parametrized safe query
}

Resources

Articles & blog posts

Standards