Introduction
SQL Injection (SQLi) is one of the most dangerous and common vulnerabilities in web applications. It allows attackers to manipulate queries executed by your database, potentially leading to unauthorized data access, modification, or even complete database takeover.
As developers, it is our responsibility to ensure that applications are secure against such attacks. In this article, we will explore practical strategies and C# code examples to prevent SQL Injection in ASP.NET MVC , ASP.NET Core MVC , and Web API applications.
What Is SQL Injection?
SQL injection happens when user input is directly concatenated into SQL statements without proper sanitization or parameterization.
Vulnerable code:
string sql = "SELECT * FROM Users WHERE Username = '" + username + "'";
If an attacker enters '; DROP TABLE Users; --
, the query becomes destructive and can delete your table.
Best Practices To Prevent SQL Injection
1. Always Use Parameterized Queries
The golden rule : never concatenate user input directly into SQL . Use parameters instead.
Secure Example (ADO.NET)
public User GetUserByName(string username)
{
const string sql = "SELECT Id, Username, Email FROM Users WHERE Username = @Username";
using (var conn = new SqlConnection(connectionString))
using (var cmd = new SqlCommand(sql, conn))
{
cmd.Parameters.Add(new SqlParameter("@Username", SqlDbType.NVarChar, 256)
{
Value = username ?? (object)DBNull.Value
});
conn.Open();
using (var reader = cmd.ExecuteReader())
{
if (reader.Read())
{
return new User
{
Id = reader.GetInt32(0),
Username = reader.GetString(1),
Email = reader.GetString(2)
};
}
}
}
return null;
}
2. Use ORM Safely (Entity Framework Core)
LINQ queries are safe by default:
var user = await _dbContext.Users
.Where(u => u.UserName == username)
.FirstOrDefaultAsync();
Unsafe (don’t do this):
var sql = $"SELECT * FROM Users WHERE UserName = '{username}'";
var users = _dbContext.Users.FromSqlRaw(sql).ToList();
Safe (use parameters):
var users = _dbContext.Users
.FromSqlInterpolated($"SELECT * FROM Users WHERE UserName = {username}")
.ToList();
3. Stored Procedures With Parameters
Stored procedures can be safe if you avoid dynamic SQL inside them.
Secure Example:
using (var cmd = new SqlCommand("usp_GetUserById", conn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("@Id", SqlDbType.Int) { Value = userId });
}
Avoid this inside SP:
EXEC('SELECT * FROM Users WHERE Username = ' + @UserName)
4. Dapper (Micro ORM)
Dapper parameterizes queries by default.
Secure Example:
var user = conn.QuerySingleOrDefault<User>(
"SELECT * FROM Users WHERE Username = @Username",
new { Username = username });
Unsafe (don’t do this):
var user = conn.QuerySingleOrDefault<User>(
$"SELECT * FROM Users WHERE Username = '{username}'");
5. Validate and Whitelist Inputs
Use length checks (e.g., max 256 chars for username).
Use type checks (ensure integers are integers).
Use whitelists for sorting, filtering, or selecting columns.
Whitelist Example:
private string GetOrderBy(string sort)
{
return sort switch
{
"name" => "Name",
"date" => "CreatedDate",
_ => "Id"
};
}
6. Example: ASP.NET Core Web API Controller
[HttpGet("search")]
public async Task<IActionResult> Search([FromQuery] string q)
{
if (string.IsNullOrWhiteSpace(q)) return BadRequest("Search term required");
var users = await _dbContext.Users
.Where(u => EF.Functions.Like(u.UserName, $"%{q}%"))
.ToListAsync();
return Ok(users);
}
Quick Checklist
Never concatenate user input into SQL.
Always use parameters in ADO.NET, EF, or Dapper.
Validate input length, type, and format.
Use whitelists for dynamic columns (e.g., sorting).
Apply the least-privilege principle to the DB account.
Log suspicious queries and monitor for attacks.
Regularly update dependencies and apply DB patches.
Conclusion
SQL injection remains a critical security risk , but by following best practices—parameterized queries, ORM safe usage, validation, stored procedures, and least privilege —you can protect your ASP.NET MVC, ASP.NET Core MVC, and Web API applications from this threat.
Security is not a one-time task but a continuous process . Always review your code, test for vulnerabilities, and apply secure coding standards across your team.