ASP.NET Core  

How to Build Secure REST APIs with ASP.NET Core

REST APIs are the backbone of modern applications, powering everything from mobile apps to enterprise integrations. But with this power comes responsibility; if APIs aren’t secure, they become an easy target for attackers.

In this article, we’ll walk through how to build secure REST APIs with ASP.NET Core , covering authentication, authorization, data protection, and real-world code samples .

Step 1: Start with HTTPS Everywhere

APIs must never be exposed over plain HTTP. Use HTTPS to encrypt communication.

In Program.cs :

  
    var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.UseHttpsRedirection(); // Force HTTPS
app.MapControllers();

app.Run();
  

Step 2: Implement Authentication with JWT

Most secure APIs rely on JWT (JSON Web Tokens) for authentication.

Configure JWT in Program.cs :

  
    builder.Services.AddAuthentication("Bearer")
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = builder.Configuration["Jwt:Issuer"],
            ValidAudience = builder.Configuration["Jwt:Audience"],
            IssuerSigningKey = new SymmetricSecurityKey(
                Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]))
        };
    });
  

Secure Controller with [Authorize] :

  
    [ApiController]
[Route("api/[controller]")]
public class OrdersController : ControllerBase
{
    [HttpGet]
    [Authorize]
    public IActionResult GetOrders()
    {
        return Ok(new { Message = "Secure orders retrieved!" });
    }
}
  

Step 3: Role-Based Authorization

Control who can access what using roles or policies.

  
    [HttpPost]
[Authorize(Roles = "Admin")]
public IActionResult CreateOrder()
{
    return Ok("Order created by Admin");
}
  

For more granular control, use policy-based authorization :

  
    builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("RequireManager", policy => policy.RequireRole("Manager"));
});
  

Step 4: Input Validation & Model Binding

Never trust user input—validate it.

  
    public class OrderModel
{
    [Required]
    public string Product { get; set; }

    [Range(1, 100)]
    public int Quantity { get; set; }
}

[HttpPost]
public IActionResult PlaceOrder([FromBody] OrderModel model)
{
    if (!ModelState.IsValid)
        return BadRequest(ModelState);

    return Ok("Order placed securely");
}
  

Step 5: Secure Cookies & Sessions (If Used)

If your API uses cookies (rare, but possible), enforce secure settings :

  
    builder.Services.AddSession(options =>
{
    options.Cookie.HttpOnly = true;
    options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
    options.Cookie.SameSite = SameSiteMode.Strict;
});
  

Step 6: Protect Against CSRF

  • For APIs with JWT tokens : not required (tokens are sent in headers).

  • For MVC + API hybrid apps: use @Html.AntiForgeryToken() and [ValidateAntiForgeryToken] .

Step 7: Secure Data at Rest & In Transit

  • Use the Data Protection API to encrypt sensitive info.

  • Apply SQL parameterization to avoid injection.

  
    var user = await _context.Users
    .FirstOrDefaultAsync(u => u.Email == email);
  

Step 8: Enable Logging & Monitoring

Log authentication failures, suspicious requests, and unusual patterns.

  
    Log.Logger = new LoggerConfiguration()
    .WriteTo.File("logs/security.log")
    .CreateLogger();
  

Integrate with Azure Monitor/Application Insights for better visibility.

Step 9: Throttle & Rate Limit

APIs without throttling are prone to DoS attacks. Use AspNetCoreRateLimit or built-in middleware.

  
    builder.Services.AddRateLimiter(options =>
{
    options.GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, string>(
        _ => RateLimitPartition.GetFixedWindowLimiter("global", _ =>
            new FixedWindowRateLimiterOptions
            {
                PermitLimit = 100,
                Window = TimeSpan.FromMinutes(1)
            }));
});
  

Step 10: Keep Dependencies & Secrets Secure

  • Run dotnet list package --vulnerable regularly.

  • Store secrets in Azure Key Vault or User Secrets (never in appsettings.json ).

  
    dotnet user-secrets set "Jwt:Key" "SuperSecretKey123!"
  

Secure API Checklist

  • Force HTTPS with redirection.

  • Use JWT for authentication.

  • Apply role/policy-based authorization.

  • Validate all input models.

  • Use parameterized queries (no raw SQL).

  • Encrypt sensitive data.

  • Enable logging & monitoring.

  • Apply rate limiting.

  • Store secrets securely.

  • Keep NuGet packages updated.

Conclusion

Building a REST API in ASP.NET Core is straightforward, but building a secure REST API requires discipline. By applying the techniques in this article—authentication, authorization, validation, encryption, logging, and monitoring you ensure your APIs are resilient against common attacks.

Security isn’t a one-time task. It’s a continuous process of auditing, patching, and improving. Start small, adopt these best practices, and make security part of your team’s development culture.