.NET Framework Security Features
.NET Framework Security Features
.NET provides comprehensive SQL injection protection across multiple data access technologies:
// Entity Framework Core - LINQ security
public class SecureEFQueries
{
private readonly ApplicationDbContext _context;
public SecureEFQueries(ApplicationDbContext context)
{
_context = context;
}
// LINQ queries are automatically parameterized
public async Task<List<User>> SearchUsersAsync(string searchTerm, List<int> roleIds)
{
var query = _context.Users
.Include(u => u.Profile)
.Include(u => u.Orders)
.AsQueryable();
if (!string.IsNullOrEmpty(searchTerm))
{
query = query.Where(u =>
u.Username.Contains(searchTerm) ||
u.Email.Contains(searchTerm) ||
u.Profile.FullName.Contains(searchTerm)
);
}
if (roleIds?.Any() == true)
{
query = query.Where(u => roleIds.Contains(u.RoleId));
}
return await query
.OrderByDescending(u => u.CreatedAt)
.Take(100)
.ToListAsync();
}
// Raw SQL with parameters
public async Task<List<OrderSummary>> GetOrderSummaryAsync(int userId, DateTime startDate)
{
var sql = @"
SELECT
DATE(OrderDate) as Date,
COUNT(*) as OrderCount,
SUM(TotalAmount) as Revenue
FROM Orders
WHERE UserId = {0}
AND OrderDate >= {1}
AND Status != 'Cancelled'
GROUP BY DATE(OrderDate)
ORDER BY Date DESC";
return await _context.OrderSummaries
.FromSqlRaw(sql, userId, startDate)
.ToListAsync();
}
// Interpolated strings for complex queries (still safe!)
public async Task<List<Product>> FindProductsAsync(ProductFilter filter)
{
FormattableString sql = $@"
SELECT * FROM Products p
WHERE p.IsActive = {true}
AND p.Price BETWEEN {filter.MinPrice} AND {filter.MaxPrice}
AND p.CategoryId IN (
SELECT CategoryId FROM Categories
WHERE ParentId = {filter.ParentCategoryId}
)";
return await _context.Products
.FromSqlInterpolated(sql)
.ToListAsync();
}
}
// Dapper with safe parameterization
public class SecureDapperQueries
{
private readonly IDbConnection _connection;
public async Task<IEnumerable<User>> SearchUsersAsync(UserSearchParams searchParams)
{
var sql = new StringBuilder("SELECT * FROM Users WHERE 1=1");
var parameters = new DynamicParameters();
if (!string.IsNullOrEmpty(searchParams.Username))
{
sql.Append(" AND Username LIKE @Username");
parameters.Add("Username", $"%{searchParams.Username}%");
}
if (searchParams.MinAge.HasValue)
{
sql.Append(" AND Age >= @MinAge");
parameters.Add("MinAge", searchParams.MinAge.Value);
}
if (searchParams.Roles?.Any() == true)
{
sql.Append(" AND RoleId IN @Roles");
parameters.Add("Roles", searchParams.Roles);
}
sql.Append(" ORDER BY CreatedAt DESC");
return await _connection.QueryAsync<User>(sql.ToString(), parameters);
}
// Using anonymous objects for parameters
public async Task<User> GetUserByIdAsync(int userId)
{
return await _connection.QuerySingleOrDefaultAsync<User>(
"SELECT * FROM Users WHERE Id = @Id AND IsActive = @IsActive",
new { Id = userId, IsActive = true }
);
}
}
Each framework provides powerful tools for SQL injection prevention, but developers must understand and use them correctly. Consistency is key—a single vulnerable query can compromise an otherwise secure application. Regular code reviews should verify that all database interactions follow framework security guidelines.