Implementation of proper input validation and the use of parameterized statements or stored procedures to prevent SQL injection attacks.
public class UserController : Controller
{
private readonly string _connectionString;
public UserController(IConfiguration configuration)
{
_connectionString = configuration.GetConnectionString("DefaultConnection");
}
[HttpGet]
public async Task GetUser(string username)
{
using (SqlConnection conn = new SqlConnection(_connectionString))
{
await conn.OpenAsync();
using (SqlCommand cmd = new SqlCommand($"SELECT * FROM Users WHERE Username = '{username}'", conn))
{
SqlDataReader reader = await cmd.ExecuteReaderAsync();
if (reader.Read())
{
return Ok(new
{
Username = reader.GetString(0),
Email = reader.GetString(1)
});
}
}
}
return NotFound();
}
}
In this piece of code, we have a method
GetUser
inside
UserController
which takes a
username
as a parameter and returns the user's details from the database.
The vulnerability lies in the SQL command that is being executed to fetch the user's details. The command is a dynamic SQL statement that directly uses the
username
input to construct the SQL query:
SqlCommand cmd = new SqlCommand($"SELECT * FROM Users WHERE Username = '{username}'", conn)
username
input to modify the SQL statement being executed, leading to a SQL Injection attack. For example, an attacker could provide a
username
like
admin'; DROP TABLE Users;--
which would result in the Users table being dropped from the database. username
input nor does it use parameterized queries or stored procedures which are recommended practices to prevent SQL Injection attacks.
public class UserController : Controller
{
private readonly string _connectionString;
public UserController(IConfiguration configuration)
{
_connectionString = configuration.GetConnectionString("DefaultConnection");
}
[HttpGet]
public async Task GetUser(string username)
{
// Validate the input
if (string.IsNullOrWhiteSpace(username))
{
return BadRequest("Invalid username");
}
using (SqlConnection conn = new SqlConnection(_connectionString))
{
await conn.OpenAsync();
// Use parameterized query to prevent SQL injection
using (SqlCommand cmd = new SqlCommand("SELECT * FROM Users WHERE Username = @Username", conn))
{
cmd.Parameters.AddWithValue("@Username", username);
SqlDataReader reader = await cmd.ExecuteReaderAsync();
if (reader.Read())
{
return Ok(new
{
Username = reader.GetString(0),
Email = reader.GetString(1)
});
}
}
}
return NotFound();
}
}
The updated code now uses a parameterized query to prevent SQL injection. This is done by replacing the inline variable in the SQL statement with a parameter (
@Username
) and then adding the value of the parameter using the
AddWithValue
method of the
SqlCommand
object. This ensures that the user input is properly escaped and prevents it from being executed as part of the SQL statement.
Additionally, the code now checks if the
username
is null or whitespace before proceeding with the SQL query. This is a basic form of input validation to ensure that only expected values are accepted.
Consider using an ORM (Object-Relational Mapping) framework like Entity Framework for more complex applications. ORMs can handle SQL queries and parameterization automatically, reducing the risk of SQL injection.
Also, implement proper error handling and logging to detect and respond to any potential SQL injection attempts. This is not shown in the code above but is an important part of secure coding practices.