This post explores essential techniques and best practices to secure data in ASP.NET Core web applications and APIs, as well as various data security standards.
How to Secure Data in ASP.NET Core Web and API
Getting Started
In today's digital landscape, ensuring data security is more important than ever. Whether developing a web application or a RESTful API using ASP.NET Core, implementing robust security measures is essential to prevent sensitive data leakage, uphold user trust, and meet regulatory requirements like GDPR or HIPAA
Here we will cover the data security standards:- Protect Communication
- Secure Headers
- Authentication and Authorization
- Protect Data
- Entity Framework Core Security Features
- Protect Sensitive Configurations
Here are the essential Data Security Standards and best practices to secure data in ASP.NET Core web applications and APIs.
Protect Communication
Unencrypted communication(HTTP) can be intercepted (Man-in-the-Middle attacks). HTTPS encrypts the data transmitted between clients and the server. Hence protect the comunication between client and server by using HTTPS (SSL/TLS), ollow the below steps to secure data in communication:
- Force HTTPS by redirecting all HTTP requests:
public void Configure(IApplicationBuilder app) { app.UseHttpsRedirection(); // ... }
- Require HTTPS globally:
services.AddMvc(options => { options.Filters.Add(new RequireHttpsAttribute()); });
- Use HSTS (HTTP Strict Transport Security):
app.UseHsts(); // Use in production only
Secure Headers
The first responsibility is to secure headers in a request. To secure HTTP headers in ASP.NET Core, you can add middleware or use built-in features to configure and send security-related headers that protect against common web vulnerabilities such as XSS, clickjacking, and MIME sniffing.
Startup.cs
or Program.cs
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
// Secure Headers Middleware
app.Use(async (context, next) =>
{
context.Response.Headers.Add("X-Content-Type-Options", "nosniff");
context.Response.Headers.Add("X-Frame-Options", "DENY");
context.Response.Headers.Add("Referrer-Policy", "no-referrer");
context.Response.Headers.Add("Permissions-Policy", "geolocation=(), microphone=()");
context.Response.Headers.Add("Cross-Origin-Opener-Policy", "same-origin");
context.Response.Headers.Add("Cross-Origin-Embedder-Policy", "require-corp");
// Content Security Policy (adjust as needed)
context.Response.Headers.Add("Content-Security-Policy",
"default-src 'self'; script-src 'self'; object-src 'none'; frame-ancestors 'none';");
await next();
});
// HSTS (use only in production over HTTPS)
app.UseHsts();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapControllers();
app.Run();
Authentication and Authorization
- Authentication
- Authenticate users using built-in providers (Identity, OAuth, JWT, etc.).
- Enable ASP.NET Core Identity: add dotnet package
Microsoft.AspNetCore.Identity.EntityFrameworkCore
and configure Identity inStartup.cs
orprogram.cs
:services.AddIdentity<ApplicationUser, IdentityRole>() .AddEntityFrameworkStores<ApplicationDbContext>();
- Enable JWT for APIs:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, // ... }; });
- Authorization
- Use role-based or policy-based authorization:
[Authorize(Roles = "Admin")] public IActionResult AdminOnly() => View();
- For APIs:
[Authorize(Policy = "CanAccessData")] [HttpGet("secure-data")] public IActionResult GetSecureData() => Ok(Data);
- Use role-based or policy-based authorization:
Protect Data using (DPAPI)
The Data Protection API(DPAPI) in ASP.NET Core is designed to provide cryptographic services for protecting data, such as encrypting cookies, tokens, or any sensitive information. It abstracts encryption logic and allows you to persist keys across deployments, use different key storage mechanisms, and manage key rotation.
Install Required Package
If you're not using a full ASP.NET Core app (e.g., minimal API or console), make sure to include the necessary package:
dotnet add package Microsoft.AspNetCore.DataProtection
Register Data Protection Services
InSetup.cs
or Program.cs
using Microsoft.AspNetCore.DataProtection;
var builder = WebApplication.CreateBuilder(args);
// Add Data Protection service
builder.Services.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo(@"./keys")) // Optional: Persist keys
.SetApplicationName("MyAppName"); // Use the same name across apps for shared protection
var app = builder.Build();
Inject and Use IDataProtector
You can use IDataProtectionProvider
to create a IDataProtector
instance and use it to protect/unprotect strings.
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("api/[controller]")]
public class SecureController : ControllerBase
{
private readonly IDataProtector _protector;
public SecureController(IDataProtectionProvider provider)
{
_protector = provider.CreateProtector("SecureController.PurposeString");
}
[HttpGet("protect")]
public IActionResult Protect([FromQuery] string input)
{
var protectedData = _protector.Protect(input);
return Ok(protectedData);
}
[HttpGet("unprotect")]
public IActionResult Unprotect([FromQuery] string protectedInput)
{
try
{
var unprotectedData = _protector.Unprotect(protectedInput);
return Ok(unprotectedData);
}
catch
{
return BadRequest("Invalid or expired protected string.");
}
}
}
Protect Sensitive Configurations
Protecting sensitive configuration data (like connection strings, API keys, secrets, etc.) in an ASP.NET Core Web and API application is critical for maintaining application security.
Use appsettings.json
Safely
Avoid storing sensitive data directly in appsettings.json
, especially in production. If you must, never commit it to source control.
{
"ConnectionStrings": {
"DefaultConnection": "YourDatabaseConnectionStringHere"
},
"ApiKeys": {
"ExternalService": "UseSecretsManagerInstead"
}
}
Do NOT Check Secrets into Source Control
Use .gitignore
to exclude files like secrets.json
or development-specific settings.
# .gitignore
secrets.json
appsettings.Development.json
Use Environment Variables
Set environment variables for your configuration data on your hosting environment or in Docker/Kubernetes.
Example: export ConnectionStrings__DefaultConnection="your-prod-connection-string"
Note:- ASP.NET Core reads __
as : when mapping keys.
Use Azure Key Vault or AWS/GCP Secret Managers
For enterprise/production apps, use a cloud secret manager:
- Azure Key Vault: ASP.NET Core built-in support
- AWS Secrets Manager: Use the AWS SDK or integrations.
- Google Secret Manager Use Google libraries and identity setup.
Protect appsettings with Encryption (Advanced)
Encrypt/decrypt parts of your configuration manually. Encrypt config sections before deployment, and decrypt them at runtime using middleware or a custom provider. But don’t roll your own crypto unless you really need to. Prefer managed services like Azure Key Vault.
Validate Configuration at Startup
Fail fast if required configuration is missing: var mySecret = builder.Configuration["MySecrets:ApiKey"];
if (string.IsNullOrEmpty(mySecret))
{
throw new Exception("Missing API Key!");
}
Role-based Configuration Access
Avoid injecting entire IConfiguration into services. Instead, bind only what's needed: builder.Services.Configure<MyApiSettings>(
builder.Configuration.GetSection("MyApiSettings"));
Then inject IOptions<MyApiSettings> into services.
Use Entity Framework Core Security Features
Entity Framework Core (EF Core) provides several security features that you can integrate into an ASP.NET Core web app or API. However, EF Core itself is primarily an Object-Relational Mapper (ORM), so its "security features" mainly focus on data access, such as:
Prevent SQL Injection
EF Core automatically uses parameterized queries, which protects against SQL Injection.
Example: var user = await context.Users
.Where(u => u.Username == username)
.FirstOrDefaultAsync();
Never use raw SQL with string concatenation
Example: context.Users.FromSqlRaw($"SELECT * FROM Users WHERE Username = '{username}'");
You can use Insted
context.Users.FromSqlRaw("SELECT * FROM Users WHERE Username = @p0", username);
Restrict Data Access Based on User
Example: [Authorize]
[HttpGet]
public async Task<IActionResult> GetUserData()
{
var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
var data = await _context.Orders
.Where(o => o.UserId == userId)
.ToListAsync();
return Ok(data);
}
Validate and Sanitize Input
Example: public class RegisterDto
{
[Required]
[EmailAddress]
public string Email { get; set; }
[Required]
[MinLength(6)]
public string Password { get; set; }
}
Avoid Overposting
Example: public class CreateUserDto
{
public string Username { get; set; }
public string Email { get; set; }
}
[HttpPost]
public async Task<IActionResult> Create(CreateUserDto dto)
{
var user = new User
{
Username = dto.Username,
Email = dto.Email
};
_context.Users.Add(user);
await _context.SaveChangesAsync();
return Ok();
}
Dont do:
[HttpPost]
public async Task<IActionResult> Create(User user) // ← Exposes everything
{
_context.Users.Add(user);
await _context.SaveChangesAsync();
return Ok();
}
Use ASP.NET Core Identity with EF Core
Example: Startup.cs (or Program.cs) builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
builder.Services.AddDefaultIdentity<IdentityUser>(options =>
options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
Cross-Site Scripting (XSS) Protection
To protect your ASP.NET Core Web Applications and APIs against Cross-Site Scripting (XSS) attacks, you need a multi-layered approach. Below are best practices and built-in features to mitigate XSS in both Razor Pages/MVC and Web APIs.
- Automatic HTML Encoding (Razor Pages / MVC Views): ASP.NET Core Razor automatically encodes output to prevent XSS.
- Tag Helpers & Form Handling: Tag Helpers encode content by default. For example:
<input asp-for="UserName" />
- Input Validation & Sanitization: Use Model Validation to validate user inputs. For rich text (HTML) inputs, sanitize it using libraries like:
var sanitizer = new HtmlSanitizer(); string safeHtml = sanitizer.Sanitize(userInput);
Summary
Securing an ASP.NET Core application is not a one-time task,it is an ongoing process. Always follow the principle of least privilege, encrypt sensitive data, and stay up to date with best practices and tools to reduce vulnerabilities.
Thanks