📄

20 API Security Best Practices

Intermediate 2 min read 300 words

20 API Security Best Practices

A comprehensive checklist for securing your APIs against common vulnerabilities and attacks.

Authentication & Authorization

1. Strong Authentication

Use OAuth 2.0 or JWT for authorized access.

// ASP.NET Core JWT Configuration
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = Configuration["Jwt:Issuer"],
            ValidAudience = Configuration["Jwt:Audience"],
            IssuerSigningKey = new SymmetricSecurityKey(
                Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))
        };
    });

2. Access Control

Define granular permissions for endpoints.

[Authorize(Policy = "AdminOnly")]
[HttpDelete("users/{id}")]
public async Task<IActionResult> DeleteUser(int id)
{
    // Only admins can delete users
}

// Policy definition
services.AddAuthorization(options =>
{
    options.AddPolicy("AdminOnly", policy =>
        policy.RequireRole("Admin"));

    options.AddPolicy("CanReadOrders", policy =>
        policy.RequireClaim("permission", "orders:read"));
});

Transport Security

3. HTTPS Encryption

Transmit data securely with HTTPS.

// Force HTTPS in ASP.NET Core
public void Configure(IApplicationBuilder app)
{
    app.UseHttpsRedirection();
    app.UseHsts();
}

// Program.cs
builder.Services.AddHttpsRedirection(options =>
{
    options.HttpsPort = 443;
});

4. Data Encryption

Encrypt sensitive data in transit and at rest.

// Encrypt sensitive data
public class EncryptionService
{
    public string Encrypt(string plainText)
    {
        using var aes = Aes.Create();
        var encryptor = aes.CreateEncryptor(key, iv);
        // ... encryption logic
    }

    public string Decrypt(string cipherText)
    {
        using var aes = Aes.Create();
        var decryptor = aes.CreateDecryptor(key, iv);
        // ... decryption logic
    }
}

Input Validation

5. Sanitize Input

Sanitize all incoming data.

public class CreateUserRequest
{
    [Required]
    [StringLength(100, MinimumLength = 2)]
    [RegularExpression(@"^[a-zA-Z\s]+$")]
    public string Name { get; set; }

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

// Custom sanitization
public static string SanitizeInput(string input)
{
    if (string.IsNullOrEmpty(input)) return input;

    return HttpUtility.HtmlEncode(input.Trim());
}

6. Secure Data Validation

Validate input and output data.

[HttpPost]
public IActionResult CreateOrder([FromBody] OrderRequest request)
{
    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }

    // Additional business validation
    if (request.Quantity <= 0 || request.Quantity > 1000)
    {
        return BadRequest("Invalid quantity");
    }

    // Process order...
}

Error Handling

7. Secure Error Messages

Avoid revealing sensitive information in errors.

// Bad - reveals internal details
catch (SqlException ex)
{
    return StatusCode(500, ex.Message); // Exposes SQL errors!
}

// Good - generic error message
catch (Exception ex)
{
    _logger.LogError(ex, "Error processing request");
    return StatusCode(500, new { message = "An error occurred" });
}

8. Disable Default Errors

Prevent revealing internal details.

// Configure exception handling
app.UseExceptionHandler(errorApp =>
{
    errorApp.Run(async context =>
    {
        context.Response.StatusCode = 500;
        context.Response.ContentType = "application/json";

        await context.Response.WriteAsJsonAsync(new
        {
            error = "An unexpected error occurred",
            requestId = Activity.Current?.Id ?? context.TraceIdentifier
        });
    });
});

Rate Limiting & Throttling

9. Rate Limiting

Prevent API abuse with rate limiting.

// ASP.NET Core 7+ Rate Limiting
builder.Services.AddRateLimiter(options =>
{
    options.AddFixedWindowLimiter("api", opt =>
    {
        opt.Window = TimeSpan.FromMinutes(1);
        opt.PermitLimit = 100;
        opt.QueueLimit = 10;
    });

    options.AddSlidingWindowLimiter("sensitive", opt =>
    {
        opt.Window = TimeSpan.FromMinutes(1);
        opt.SegmentsPerWindow = 6;
        opt.PermitLimit = 10;
    });
});

[EnableRateLimiting("api")]
[HttpGet("products")]
public IActionResult GetProducts() { }

10. Throttle Login Attempts

Prevent brute-force attacks.

public class LoginAttemptService
{
    private readonly IMemoryCache _cache;

    public bool IsBlocked(string ipAddress)
    {
        var key = $"login_attempts:{ipAddress}";
        var attempts = _cache.GetOrCreate(key, entry =>
        {
            entry.AbsoluteExpiration = DateTimeOffset.Now.AddMinutes(15);
            return 0;
        });

        return attempts >= 5;
    }

    public void RecordFailedAttempt(string ipAddress)
    {
        var key = $"login_attempts:{ipAddress}";
        _cache.Set(key, _cache.Get<int>(key) + 1,
            DateTimeOffset.Now.AddMinutes(15));
    }
}

Logging & Monitoring

11. Logging and Auditing

Maintain comprehensive logs.

public class AuditMiddleware
{
    public async Task InvokeAsync(HttpContext context)
    {
        var auditLog = new AuditLog
        {
            Timestamp = DateTime.UtcNow,
            UserId = context.User?.Identity?.Name,
            IpAddress = context.Connection.RemoteIpAddress?.ToString(),
            Method = context.Request.Method,
            Path = context.Request.Path,
            UserAgent = context.Request.Headers.UserAgent
        };

        _logger.LogInformation("API Request: {@AuditLog}", auditLog);

        await _next(context);
    }
}

12. Security Testing

Regularly assess for vulnerabilities.

// Integration tests for security
[Fact]
public async Task UnauthorizedAccess_Returns401()
{
    var response = await _client.GetAsync("/api/admin/users");
    Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
}

[Fact]
public async Task SqlInjection_IsPrevented()
{
    var maliciousInput = "'; DROP TABLE Users; --";
    var response = await _client.GetAsync($"/api/users?search={maliciousInput}");
    Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
}

Token Management

13. Token Expiration

Set short-lived access tokens.

var tokenDescriptor = new SecurityTokenDescriptor
{
    Subject = new ClaimsIdentity(claims),
    Expires = DateTime.UtcNow.AddMinutes(15), // Short-lived
    SigningCredentials = credentials
};

// Use refresh tokens for longer sessions
public class RefreshTokenService
{
    public string GenerateRefreshToken()
    {
        var randomBytes = new byte[64];
        using var rng = RandomNumberGenerator.Create();
        rng.GetBytes(randomBytes);
        return Convert.ToBase64String(randomBytes);
    }
}

14. Use CSRF Tokens

Prevent unauthorized requests.

// Add anti-forgery token
services.AddAntiforgery(options =>
{
    options.HeaderName = "X-CSRF-TOKEN";
    options.Cookie.Name = "CSRF-TOKEN";
    options.Cookie.HttpOnly = true;
    options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
});

// Validate in controller
[ValidateAntiForgeryToken]
[HttpPost]
public IActionResult ProcessPayment(PaymentRequest request) { }

Security Headers

15. Security Headers

Use CSP and X-XSS-Protection.

// Add security 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("X-XSS-Protection", "1; mode=block");
    context.Response.Headers.Add("Referrer-Policy", "strict-origin-when-cross-origin");
    context.Response.Headers.Add("Content-Security-Policy",
        "default-src 'self'; script-src 'self'");

    await next();
});

16. CORS Configuration

Restrict cross-origin requests.

services.AddCors(options =>
{
    options.AddPolicy("Production", builder =>
    {
        builder
            .WithOrigins("https://myapp.com", "https://admin.myapp.com")
            .WithMethods("GET", "POST", "PUT", "DELETE")
            .WithHeaders("Authorization", "Content-Type")
            .SetPreflightMaxAge(TimeSpan.FromMinutes(10));
    });
});

API Design

17. API Versioning

Gracefully handle changes and backward compatibility.

services.AddApiVersioning(options =>
{
    options.DefaultApiVersion = new ApiVersion(1, 0);
    options.AssumeDefaultVersionWhenUnspecified = true;
    options.ReportApiVersions = true;
    options.ApiVersionReader = ApiVersionReader.Combine(
        new UrlSegmentApiVersionReader(),
        new HeaderApiVersionReader("X-Api-Version")
    );
});

[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/[controller]")]
public class UsersController : ControllerBase { }

18. Safe API Documentation

Avoid revealing sensitive information.

// Swagger configuration - hide sensitive endpoints
services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new OpenApiInfo { Title = "API", Version = "v1" });

    // Don't expose internal endpoints
    c.DocInclusionPredicate((docName, apiDesc) =>
    {
        return !apiDesc.RelativePath.Contains("internal");
    });
});

Session Management

19. Secure Session Management

Invalidate sessions securely.

public async Task Logout()
{
    // Invalidate refresh token
    await _tokenService.RevokeRefreshToken(userId);

    // Clear session
    HttpContext.Session.Clear();

    // Sign out
    await HttpContext.SignOutAsync();
}

// Token revocation
public class TokenRevocationService
{
    private readonly IDistributedCache _cache;

    public async Task RevokeToken(string tokenId, TimeSpan expiration)
    {
        await _cache.SetStringAsync(
            $"revoked:{tokenId}",
            "revoked",
            new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = expiration }
        );
    }
}

20. Regular Updates

Keep API up-to-date with patches.

<!-- Enable automatic NuGet updates in CI/CD -->
<PropertyGroup>
    <EnableNETAnalyzers>true</EnableNETAnalyzers>
    <AnalysisLevel>latest</AnalysisLevel>
</PropertyGroup>

<!-- Use Dependabot or similar for dependency updates -->

Security Checklist

Category Item Priority
Auth OAuth 2.0 / JWT Critical
Auth Role-based access control Critical
Transport HTTPS everywhere Critical
Input Input validation Critical
Input SQL injection prevention Critical
Errors Generic error messages High
Rate Limiting API throttling High
Logging Audit logging High
Headers Security headers Medium
Testing Regular security audits Medium

Sources

  • APIs/API security.jpg