SQL Server Security Best Practices
Protect your database and sensitive data with these security configurations.
1. Connection String Security
Never Store Credentials in Code
// BAD - Hardcoded credentials
var connectionString = "Server=myserver;Database=mydb;User Id=admin;Password=secret123;";
// GOOD - Use configuration
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
Use Environment Variables
// appsettings.json (template only)
{
"ConnectionStrings": {
"DefaultConnection": "Server=myserver;Database=mydb;User Id=${DB_USER};Password=${DB_PASSWORD};"
}
}
2. Prevent SQL Injection
Always Use Parameterized Queries
// BAD - SQL Injection vulnerable
string query = $"SELECT * FROM Users WHERE Username = '{username}'";
// GOOD - Parameterized query
string query = "SELECT * FROM Users WHERE Username = @username";
using var command = new SqlCommand(query, connection);
command.Parameters.AddWithValue("@username", username);
Entity Framework Core (Safe by Default)
// EF Core uses parameters automatically
var user = await context.Users
.Where(u => u.Username == username)
.FirstOrDefaultAsync();
// Avoid raw SQL unless necessary
// If you must use raw SQL, still parameterize:
var users = await context.Users
.FromSqlRaw("SELECT * FROM Users WHERE Username = {0}", username)
.ToListAsync();
3. Database User Permissions
Principle of Least Privilege
- Create dedicated database users per application
- Grant only required permissions (SELECT, INSERT, UPDATE, DELETE)
- Never use 'sa' or admin accounts in applications
- Use separate accounts for read-only operations
Permission Levels
| Role | Use Case | Permissions |
|---|---|---|
| db_datareader | Read-only applications, reporting | SELECT only |
| db_datawriter | Data entry applications | INSERT, UPDATE, DELETE |
| db_owner | Development only, never production | Full control |
4. Encrypt Sensitive Data
Encrypt Connection
Server=myserver;Database=mydb;User Id=user;Password=pass;Encrypt=True;TrustServerCertificate=False;
Encrypt Sensitive Columns
For highly sensitive data, consider:
- Application-level encryption before storing
- SQL Server Always Encrypted (if available)
- Hashing for passwords (never store plain text)
Password Hashing Example
// ASP.NET Core Identity handles this automatically // For custom implementation: using Microsoft.AspNetCore.Identity; var hasher = new PasswordHasher<User>(); string hashedPassword = hasher.HashPassword(user, plainPassword); // Verify password var result = hasher.VerifyHashedPassword(user, hashedPassword, inputPassword);
5. Audit and Monitoring
Log Database Access
// Enable EF Core logging
services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(connectionString)
.LogTo(Console.WriteLine, LogLevel.Information));
Track Changes
- Implement audit tables for sensitive data changes
- Record who changed what and when
- Review logs regularly for suspicious activity
Security Checklist
- ☐ Credentials stored in configuration, not code
- ☐ All queries use parameters (no string concatenation)
- ☐ Database user has minimal required permissions
- ☐ Connection uses encryption
- ☐ Passwords are hashed, never stored plain text
- ☐ Sensitive data is encrypted at rest
- ☐ Regular backups are in place
- ☐ Access logs are reviewed