ADO.NET · Executing Commands

SQL Injection Prevention

SQL injection lets an attacker rewrite your query by typing into a form. It is still one of the most damaging web vulnerabilities — and completely preventable.

Intermediate 5 min Lesson 12 of 32Examples use Microsoft.Data.SqlClient on .NET 9 with SQL Server 2022.
Before you start:Parameterized Queries
0/32 in ADO.NET
On this page · 5 min · Intermediate

In simple words#

SQL injection is someone writing extra instructions inside a form box (“…and give me all passwords”). Parameters treat whatever they write as plain text, never as instructions.

SQL injection
Attacker input changes your SQL.
Fix
Parameters for every value.
Whitelist
For column names you cannot parameterise.

Learning objectives#

  • See how injection happens
  • Fix it with parameters
  • Handle dynamic sort columns safely
  • Add defence in depth

Explanation#

Injection happens when user input is concatenated into SQL text. Input such as ' OR '1'='1 changes the meaning of the query.

Parameters fix it completely for values, because values are never parsed as SQL.

Table names, column names and sort directions cannot be parameters. Map user choices to a fixed whitelist instead.

Stored procedures are safe only if they do not build dynamic SQL from their inputs. Use sp_executesql with parameters if they must.

Defence in depth: the app’s login should have only the permissions it needs — never sysadmin or db_owner.

Code example: Vulnerable vs safe#

C#Login.cs Illustrative
// ❌ VULNERABLE: input "x' OR '1'='1" logs in as the first user
var bad = 
quot;SELECT Id FROM Users WHERE Email = '
{email}' AND PasswordHash = '{hash}'";
// ✅ SAFE: values are parameters await using var cmd = new SqlCommand("SELECT Id FROM Users WHERE Email = @email", conn); cmd.Parameters.Add("@email", SqlDbType.NVarChar, 256).Value = email; // ✅ SAFE dynamic ORDER BY: whitelist, never raw input string orderBy = sort switch { "price" => "Price", "name" => "Name", _ => "Id", }; string dir = descending ? "DESC" : "ASC"; var listSql =
quot;SELECT Id, Name, Price FROM Products ORDER BY
{orderBy} {dir}";
Expected output
The vulnerable query returns a user for any password. The safe versions treat input only as data, and ORDER BY can only ever be one of three known columns.

This example is illustrative. Copy or download it and run it in your own environment; the expected output is shown above.

Step-by-step explanation#

  1. String interpolation puts attacker text directly into the SQL.
  2. The parameterised query compares Email with the input as a value.
  3. The switch maps user input to known column names; anything else falls back to Id.
  4. Verify the password hash in C# with a proper hashing library, not in SQL text.

Real-world use case#

A security review greps the codebase for "SqlCommand(" with

quot; or + and fixes every match — a quick win before a penetration test.

Common mistakes#

Escaping quotes by hand

Replace("'", "''") misses edge cases and encodings. Use parameters.

Trusting values from dropdowns or hidden fields

Attackers send any request they like. Treat all input as untrusted.

Best practices#

  • Parameters for every value
  • Whitelist identifiers
  • Least-privilege database logins
  • Code review and static analysis for concatenated SQL

Remember this#

Never concatenate input into SQL.

Values → parameters. Names → whitelist.

App login ≠ db_owner.

Practice exercise#

Your task: Fix this code: new SqlCommand("DELETE FROM Products WHERE Id = " + id, conn).

Show solution
C# Illustrative
await using var cmd = new SqlCommand("DELETE FROM Products WHERE Id = @id", conn);
cmd.Parameters.Add("@id", SqlDbType.Int).Value = id;

Mini quiz#

SQL Injection Prevention quizMultiple choice Question 1 of 1

How should a user-chosen sort column be handled?

Score so far: 0/0

Want more? Practise all ADO.NET MCQs · ADO.NET interview questions

My notes#

Saved in this browser.

Progress is saved in this browser. Sign in to sync.