liveBook · Manning

SQL injection

As explained in .NET in Action, Second Edition

A SQL injection vulnerability is a weakness that allows crafted input to alter or execute unintended SQL statements.

SQL injection is a risk at the boundary between a service and its database: crafted input may change the SQL the database receives. EF Core helps guard against it, but using EF Core alone is not a reason to skip an audit. Treat the database connection and its requests and responses as part of the application’s threat model. section 9.4 section 9.2

A practical check follows the SQL from application code to the database:

  • Search for hand-written SQL calls, including FromSqlRaw, FromSqlInterpolated, and Database.ExecuteSqlCommand. Review how input reaches each statement. section 9.4.1
  • Exercise database operations in tests and attach a DbCommandInterceptor to the DbContext. Log each DbCommand.CommandText before EF Core sends it to the data store. section 9.4.1
  • Inspect the generated statements rather than assuming every LINQ query takes the same shape. A lookup by ID uses a parameter such as @__id_0; a collection-based title lookup is shown with values in the SQL instead. section 9.4.1

An alarming-looking value is not proof of execution

A title list contains `Robert'); DROP TABLE Students;--`. In the generated `IN` clause, EF Core doubles the embedded quote, leaving that text inside a SQL string value. Inspect the complete statement to distinguish an attack-shaped input from SQL that actually changes the query’s structure. section 9.4.1

Listing 9.6 LogSqlInterceptor logging SQL generated by EF Core
using System.Data.Common;                                  #1
using Microsoft.EntityFrameworkCore.Diagnostics;           #2
using Xunit.Abstractions;                                  #3
  
namespace ManningBooksApi.Tests;
  
public class LogSqlInterceptor : DbCommandInterceptor
{
  private readonly ITestOutputHelper _testOutput;          #4
  
  public LogSqlInterceptor(ITestOutputHelper testOutput)
    => _testOutput = testOutput;
  
  public override
    ValueTask<InterceptionResult<DbDataReader>>            #5
    ReaderExecutingAsync(                                  #6
      DbCommand command,
      CommandEventData eventData,
      InterceptionResult<DbDataReader> result,
      CancellationToken cancelToken)
  {
    _testOutput.WriteLine(command.CommandText.Trim());     #7
    return base.ReaderExecutingAsync(                      #8
      command, eventData, result, cancelToken);
  }
}

The interceptor implementation shows where to capture SQL and how to send it to xUnit’s test output, avoiding mixed console logs from parallel tests. section 9.4.1

Generated SQL can also vary by EF Core version: an EF Core 8 example passes a title list as a JSON parameter and reads it with OPENJSON. The useful habit is to trust the framework’s protections while verifying the statements produced by the operations you use. section 9.4.1 section 9.7

How .NET in Action, Second Edition covers it

How deep it goes
Works through an interceptor-based SQL audit and contrasting generated queries, without establishing that every query shape is safe.
Examples
Uses an ID lookup, a malicious-looking title in an IN clause, and an EF Core 8 JSON-parameter query.
What it assumes
Assumes working familiarity with C#, EF Core database access, and tests; demonstrates how to add a SQL-logging interceptor.
What you can do after
Diagnose/debug with it by auditing raw SQL calls and inspecting statements captured in tests before database execution.
How it connects
Connects SQL injection to threat modeling, EF Core, LINQ, raw SQL, parameters, and automated tests.

Also explained in

Compare how 9 books explain SQL injection →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of .NET in Action, Second Edition. The section links point to the parts of the book it draws on.