Skip to main content

What is API Security?

API Security, sometimes referred to as Web API Protection or REST API Security, is the practice of protecting application programming interfaces from malicious attacks and misuse. Its core purpose is to ensure that only authorized users and applications can access your API endpoints, that data remains confidential and intact, and that the service remains available to legitimate users.
It solves the fundamental problem of securing the communication channel between disparate systems in a world where APIs have become the primary method of system integration.

How it works in C#

Input Validation

Explanation: Input validation ensures that incoming data conforms to expected formats, types, and ranges before processing. It’s the first line of defense against injection attacks and data corruption.
public class UserController : ControllerBase
{
    [HttpPost]
    public IActionResult CreateUser([FromBody] UserDto userDto)
    {
        // Model-based validation using DataAnnotations
        if (!ModelState.IsValid)
            return BadRequest(ModelState);

        // Custom validation logic
        if (userDto.Email.Contains("+") && !IsAllowedPlusEmail(userDto.Email))
            return BadRequest("Plus addressing not allowed for this domain");

        // Whitelist validation for specific formats
        if (!Regex.IsMatch(userDto.PhoneNumber, @"^\d{10}$"))
            return BadRequest("Phone number must be 10 digits");

        // Business logic validation
        if (userDto.Age < 18 || userDto.Age > 120)
            return BadRequest("Age must be between 18 and 120");

        // Proceed with processing valid data
        var user = _userService.CreateUser(userDto);
        return Ok(user);
    }
}

// Data transfer object with validation attributes
public class UserDto
{
    [Required]
    [StringLength(100, MinimumLength = 2)]
    public string Name { get; set; }

    [Required]
    [EmailAddress]
    public string Email { get; set; }

    [Range(18, 120)]
    public int Age { get; set; }
}

Rate Limiting

Explanation: Rate limiting controls how many requests a client can make to your API within a specific timeframe, preventing abuse and ensuring service availability.
// Using ASP.NET Core built-in rate limiting
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRateLimiter(options =>
{
    // Global policy
    options.GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, string>(httpContext =>
        RateLimitPartition.GetFixedWindowLimiter(
            partitionKey: httpContext.Request.Headers["X-API-Key"].FirstOrDefault() ?? httpContext.Connection.RemoteIpAddress?.ToString(),
            factory: partition => new FixedWindowRateLimiterOptions
            {
                AutoReplenishment = true,
                PermitLimit = 100,  // 100 requests
                Window = TimeSpan.FromMinutes(1)  // per minute
            }));

    // Specific endpoint policy
    options.AddPolicy("StrictPolicy", context =>
        RateLimitPartition.GetFixedWindowLimiter(
            partitionKey: context.Connection.RemoteIpAddress?.ToString(),
            factory: partition => new FixedWindowRateLimiterOptions
            {
                PermitLimit = 10,
                Window = TimeSpan.FromMinutes(1)
            }));
});

var app = builder.Build();
app.UseRateLimiter();

// Apply strict policy to sensitive endpoint
app.MapGet("/api/admin/users", () => "Admin endpoint")
   .RequireRateLimiting("StrictPolicy");

CSRF (Cross-Site Request Forgery)

Explanation: CSRF protection prevents malicious websites from making unauthorized requests on behalf of authenticated users.
var builder = WebApplication.CreateBuilder(args);

// Add antiforgery service
builder.Services.AddAntiforgery(options =>
{
    options.HeaderName = "X-XSRF-TOKEN";  // Common header name
    options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
});

var app = builder.Build();

// Apply antiforgery globally
app.Use(async (context, next) =>
{
    // Skip antiforgery for GET, HEAD, OPTIONS, TRACE
    if (!context.Request.Method.Equals("GET", StringComparison.OrdinalIgnoreCase))
    {
        await context.RequestServices.GetRequiredService<IAntiforgery>()
            .ValidateRequestAsync(context);
    }
    await next();
});

// In your controller
[AutoValidateAntiforgeryToken]  // Automatically validates for non-GET requests
public class OrderController : Controller
{
    [HttpPost]
    public IActionResult CreateOrder(OrderDto order)
    {
        // Token is automatically validated by the attribute
        _orderService.CreateOrder(order);
        return Ok();
    }
}

// Client-side: Include token in requests
// Typically handled automatically by frameworks like Angular

CORS (Cross-Origin Resource Sharing)

Explanation: CORS controls which external domains can access your API resources, preventing unauthorized cross-origin requests.
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    // Named policy for specific origins
    options.AddPolicy("AllowSpecificOrigins", policy =>
    {
        policy.WithOrigins("https://trusted-domain.com", "https://app.example.com")
              .AllowAnyHeader()
              .AllowAnyMethod()
              .AllowCredentials();  // If using cookies/auth
    });

    // Policy for development
    options.AddPolicy("DevelopmentPolicy", policy =>
    {
        policy.AllowAnyOrigin()  // Use only in development!
              .AllowAnyHeader()
              .AllowAnyMethod();
    });

    // Restricted policy for production APIs
    options.AddPolicy("RestrictedApi", policy =>
    {
        policy.WithOrigins("https://api-consumer.com")
              .WithMethods("GET", "POST")
              .WithHeaders("Content-Type", "Authorization");
    });
});

var app = builder.Build();

// Apply CORS middleware
app.UseCors();  // Applies the default policy

// Apply specific policy to endpoint
app.MapGet("/api/public/data", () => "Public data")
   .RequireCors("AllowSpecificOrigins");

SQL Injection Prevention

Explanation: SQL injection prevention involves using parameterized queries and ORMs to safely handle user input in database operations.
public class UserRepository : IUserRepository
{
    private readonly ApplicationDbContext _context;

    // SAFE: Using Entity Framework (parameterized queries)
    public async Task<User> GetUserByEmailAsync(string email)
    {
        // Entity Framework uses parameterized queries internally
        return await _context.Users
            .FirstOrDefaultAsync(u => u.Email == email);
    }

    // SAFE: Raw SQL with parameters
    public async Task<User> GetUserByIdAsync(int id)
    {
        return await _context.Users
            .FromSqlRaw("SELECT * FROM Users WHERE Id = {0}", id)
            .FirstOrDefaultAsync();
    }

    // SAFE: Using Dapper with parameters
    public async Task<User> GetUserByNameAsync(string name)
    {
        using var connection = new SqlConnection(_connectionString);
        return await connection.QueryFirstOrDefaultAsync<User>(
            "SELECT * FROM Users WHERE Name = @UserName", 
            new { UserName = name });
    }

    // UNSAFE: String concatenation (DON'T DO THIS!)
    public async Task<User> GetUserUnsafe(string name)
    {
        // Vulnerable to SQL injection!
        var sql = $"SELECT * FROM Users WHERE Name = '{name}'";
        return await _context.Users.FromSqlRaw(sql).FirstOrDefaultAsync();
    }
}

Encryption

Explanation: Encryption protects data confidentiality by converting plaintext into ciphertext using cryptographic algorithms.
public class EncryptionService
{
    private readonly byte[] _key;  // 256-bit key
    private readonly byte[] _iv;   // 128-bit IV

    public EncryptionService(IConfiguration config)
    {
        _key = Convert.FromBase64String(config["EncryptionKey"]);
        _iv = Convert.FromBase64String(config["EncryptionIV"]);
    }

    public string Encrypt(string plainText)
    {
        using var aes = Aes.Create();
        using var encryptor = aes.CreateEncryptor(_key, _iv);
        using var memoryStream = new MemoryStream();
        using var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);
        
        using (var writer = new StreamWriter(cryptoStream))
        {
            writer.Write(plainText);
        }
        
        return Convert.ToBase64String(memoryStream.ToArray());
    }

    public string Decrypt(string cipherText)
    {
        var buffer = Convert.FromBase64String(cipherText);
        
        using var aes = Aes.Create();
        using var decryptor = aes.CreateDecryptor(_key, _iv);
        using var memoryStream = new MemoryStream(buffer);
        using var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read);
        using var reader = new StreamReader(cryptoStream);
        
        return reader.ReadToEnd();
    }

    // Hashing passwords (one-way encryption)
    public string HashPassword(string password)
    {
        return BCrypt.Net.BCrypt.HashPassword(password, workFactor: 12);
    }

    public bool VerifyPassword(string password, string hash)
    {
        return BCrypt.Net.BCrypt.Verify(password, hash);
    }
}

Secure Headers

Explanation: Secure HTTP headers provide additional security controls by instructing browsers how to behave when handling your API responses.
public class SecurityHeadersMiddleware
{
    private readonly RequestDelegate _next;

    public SecurityHeadersMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        // Add security headers to response
        context.Response.Headers.Append("X-Content-Type-Options", "nosniff");
        context.Response.Headers.Append("X-Frame-Options", "DENY");
        context.Response.Headers.Append("X-XSS-Protection", "1; mode=block");
        context.Response.Headers.Append("Referrer-Policy", "strict-origin-when-cross-origin");
        
        // Content Security Policy for APIs that serve HTML
        context.Response.Headers.Append("Content-Security-Policy", 
            "default-src 'self'; script-src 'none'; object-src 'none'");
        
        // Remove server identification header
        context.Response.Headers.Remove("Server");
        
        await _next(context);
    }
}

// Registration
var app = builder.Build();
app.UseMiddleware<SecurityHeadersMiddleware>();

// Alternative: Using built-in middleware for some headers
app.UseHsts();  // HTTP Strict Transport Security
app.UseHttpsRedirection();  // Redirect HTTP to HTTPS

Why is API Security important?

  1. Defense in Depth Principle: Implementing multiple layers of security controls ensures that if one layer fails, others provide backup protection, creating a resilient security posture.
  2. Principle of Least Privilege: Proper API security enforces that users and systems only have access to the resources absolutely necessary for their function, minimizing potential damage from breaches.
  3. Fail-Safe Defaults Principle: Security-first API design ensures that systems default to secure states, requiring explicit permissioning rather than assuming trust, which prevents accidental exposure.

Advanced Nuances

1. Advanced Rate Limiting with Redis Distribution

For scalable applications, implement distributed rate limiting using Redis to synchronize limits across multiple API instances:
builder.Services.AddSingleton<IConnectionMultiplexer>(ConnectionMultiplexer.Connect("redis-connection"));
builder.Services.AddRateLimiter(options =>
{
    options.AddRedisRateLimiter("DistributedPolicy", (context) =>
        RateLimitPartition.GetTokenBucketLimiter(
            partitionKey: context.Connection.RemoteIpAddress?.ToString(),
            factory: partition => new TokenBucketRateLimiterOptions
            {
                TokenLimit = 1000,
                TokensPerPeriod = 100,
                ReplenishmentPeriod = TimeSpan.FromMinutes(1)
            }));
});

2. Dynamic CORS Policies

Implement dynamic CORS resolution based on request context, such as different policies for different API versions or client types:
public class DynamicCorsPolicyProvider : ICorsPolicyProvider
{
    public Task<CorsPolicy?> GetPolicyAsync(HttpContext context, string? policyName)
    {
        var requestedOrigin = context.Request.Headers["Origin"];
        
        // Validate origin against database or configuration
        if (IsOriginAllowed(requestedOrigin))
        {
            return Task.FromResult<CorsPolicy?>(new CorsPolicy
            {
                Origins = { requestedOrigin },
                Methods = { "GET", "POST" },
                Headers = { "Authorization", "Content-Type" }
            });
        }
        
        return Task.FromResult<CorsPolicy?>(null);
    }
}

3. Context-Aware Input Validation

Advanced validation that considers business context and user permissions:
public class ContextAwareValidator : IEndpointFilter
{
    public async ValueTask<object?> InvokeAsync(
        EndpointFilterInvocationContext context, 
        EndpointFilterDelegate next)
    {
        var user = context.HttpContext.User;
        var dto = context.Arguments.OfType<IValidatableDto>().FirstOrDefault();
        
        if (dto != null)
        {
            // Business context validation
            if (dto is OrderDto order && !user.IsInRole("Admin"))
            {
                if (order.TotalAmount > user.GetMaxOrderAmount())
                    return Results.BadRequest("Order amount exceeds limit");
            }
        }
        
        return await next(context);
    }
}

How this fits the Roadmap

Within the “Security and Best Practices” section of the Advanced CSharp Mastery roadmap, API Security serves as the fundamental building block upon which more advanced security concepts are built. It’s the prerequisite for understanding:
  • Advanced Authentication & Authorization: JWT, OAuth 2.0, OpenID Connect implementations
  • API Gateway Security Patterns: Circuit breakers, API versioning, and advanced throttling
  • Microservices Security: Service-to-service authentication, secret management, and secure inter-service communication
  • Security Monitoring & Auditing: Implementing comprehensive logging, intrusion detection, and security event correlation
Mastering API Security unlocks the ability to design enterprise-grade secure systems and prepares you for advanced topics like zero-trust architectures, security-by-design principles, and compliance-driven security implementations required in senior-level positions.

Build docs developers (and LLMs) love