I'll provide an example to demonstrate how SQL injection works and how it can be exploited, but I
must emphasize that this is for educational purposes only. SQL injection is a serious security
vulnerability, and exploiting it in a real system without permission is illegal and unethical. Always
ensure your applications are secure against such attacks.
In this example, we'll create a simple ASP.NET application (continuing from your previous
steampunk-style login setup) that is vulnerable to SQL injection, and then I'll show how an attacker
might exploit it. I'll also briefly explain how to prevent it.
1. Set Up a Vulnerable ASP.NET Application
We'll modify the login functionality from the previous example to use a database and make it
vulnerable to SQL injection by using string concatenation in the SQL query.
Prerequisites
• SQL Server (or any database like SQLite for simplicity).
• Install the Microsoft.EntityFrameworkCore.SqlServer NuGet package for database access.
2. Set Up the Database
Create a simple database with a Users table.
SQL Script to Create the Table
sql
CREATE TABLE Users (
Id INT PRIMARY KEY IDENTITY(1,1),
Username NVARCHAR(50) NOT NULL,
Password NVARCHAR(50) NOT NULL,
Email NVARCHAR(100) NOT NULL
);
-- Insert a test user
INSERT INTO Users (Username, Password, Email)
VALUES ('testuser', 'password123', '
[email protected]');
3. Update the Application
Add a database context using Entity Framework Core.
AppDbContext.cs
csharp
using Microsoft.EntityFrameworkCore;
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
{ }
public DbSet<User> Users { get; set; }
}
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public string Email { get; set; }
}
Add the connection string to appsettings.json:
json
{
"ConnectionStrings": {
"DefaultConnection":
"Server=your_server;Database=YourDatabase;Trusted_Connection=True;"
}
}
Register the DbContext in Program.cs:
csharp
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnectio
n")));
4. Modify the Login Controller to Be Vulnerable
We'll modify the AccountController to use a raw SQL query with string concatenation, which makes
it vulnerable to SQL injection.
AccountController.cs (Vulnerable Version)
csharp
using Microsoft.AspNetCore.Mvc;
using Microsoft.Data.SqlClient;
using System.Threading.Tasks;
using YourApp.Models;
using YourApp.Services;
namespace YourApp.Controllers
{
public class AccountController : Controller
{
private readonly IEmailService _emailService;
private readonly string _connectionString;
public AccountController(IEmailService emailService, IConfiguration
config)
{
_emailService = emailService;
_connectionString = config.GetConnectionString("DefaultConnection");
}
public IActionResult Login()
{
return View();
}
[HttpPost]
public async Task<IActionResult> Login(LoginModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
// Vulnerable SQL query using string concatenation
string query = $"SELECT * FROM Users WHERE Username =
'{model.Username}' AND Password = '{model.Password}'";
using var connection = new SqlConnection(_connectionString);
await connection.OpenAsync();
using var command = new SqlCommand(query, connection);
using var reader = await command.ExecuteReaderAsync();
if (reader.HasRows)
{
await reader.ReadAsync();
string email = reader["Email"].ToString();
// Generate and send 2FA code
string twoFactorCode = new Random().Next(100000,
999999).ToString();
TempData["TwoFactorCode"] = twoFactorCode;
TempData["Username"] = model.Username;
await _emailService.SendEmailAsync(email, "Your 2FA Code",
$"Your 2FA code is: {twoFactorCode}");
return RedirectToAction("TwoFactor");
}
ModelState.AddModelError("", "Invalid username or password.");
return View(model);
}
// TwoFactor methods remain the same as in the previous example
public IActionResult TwoFactor()
{
var model = new TwoFactorModel { Username =
TempData["Username"]?.ToString() };
return View(model);
}
[HttpPost]
public IActionResult TwoFactor(TwoFactorModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
string storedCode = TempData["TwoFactorCode"]?.ToString();
if (model.Code == storedCode)
{
return RedirectToAction("Index", "Home");
}
ModelState.AddModelError("", "Invalid 2FA code.");
return View(model);
}
}
}
5. SQL Injection Example
The query in the Login action is vulnerable because it directly concatenates user input into the SQL
query:
sql
SELECT * FROM Users WHERE Username = '{model.Username}' AND Password =
'{model.Password}'
An attacker can exploit this by entering malicious input. For example:
• Username: testuser' --
• Password: (leave blank or any value)
The resulting SQL query becomes:
sql
SELECT * FROM Users WHERE Username = 'testuser' --' AND Password = ''
• The -- is a SQL comment that ignores the rest of the query, so the password check is
bypassed.
• If testuser exists in the database, the attacker logs in without knowing the password.
6. More Dangerous SQL Injection
An attacker could also use SQL injection to extract data or modify the database. For example:
• Username: testuser'; DROP TABLE Users; --
• Password: (any value)
The query becomes:
sql
SELECT * FROM Users WHERE Username = 'testuser'; DROP TABLE Users; --' AND
Password = ''
This would delete the Users table entirely, causing significant damage.
7. How to Prevent SQL Injection
To fix this vulnerability, use parameterized queries or an ORM like Entity Framework Core. Here's
the secure version of the Login action:
AccountController.cs (Secure Version)
csharp
[HttpPost]
public async Task<IActionResult> Login(LoginModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
// Use parameterized query to prevent SQL injection
string query = "SELECT * FROM Users WHERE Username = @Username AND Password
= @Password";
using var connection = new SqlConnection(_connectionString);
await connection.OpenAsync();
using var command = new SqlCommand(query, connection);
command.Parameters.AddWithValue("@Username", model.Username);
command.Parameters.AddWithValue("@Password", model.Password);
using var reader = await command.ExecuteReaderAsync();
if (reader.HasRows)
{
await reader.ReadAsync();
string email = reader["Email"].ToString();
string twoFactorCode = new Random().Next(100000, 999999).ToString();
TempData["TwoFactorCode"] = twoFactorCode;
TempData["Username"] = model.Username;
await _emailService.SendEmailAsync(email, "Your 2FA Code", $"Your 2FA
code is: {twoFactorCode}");
return RedirectToAction("TwoFactor");
}
ModelState.AddModelError("", "Invalid username or password.");
return View(model);
}
Alternatively, use Entity Framework Core to avoid raw SQL entirely:
csharp
var user = await _context.Users
.FirstOrDefaultAsync(u => u.Username == model.Username && u.Password ==
model.Password);
if (user != null)
{
// Proceed with 2FA
}
8. Additional Security Tips
• Use ASP.NET Identity for user management, which includes built-in protections.
• Hash passwords instead of storing them in plain text.
• Validate and sanitize all user inputs.
• Use least privilege for database accounts.
Summary
This example showed how SQL injection can bypass authentication or cause damage by exploiting
a poorly constructed SQL query. Always use parameterized queries or an ORM to prevent SQL
injection in your applications. If you'd like to add steampunk styling to this login page or explore
more security measures, let me know!