APIs are critical components of modern applications, enabling communication between different systems. However, they are also frequent targets for unauthorized access and abuse. Securing your API requires a layered approach that combines CORS validation, strong authentication mechanisms, and robust monitoring. In this article, we will cover several strategies to protect your API and ensure it is only accessible by trusted clients.
1. Configure CORS Properly
Cross-Origin Resource Sharing (CORS) is an essential layer of security that determines which origins are allowed to interact with your API. Configuring CORS correctly is vital to prevent unauthorized access.
Example in ASP.NET Core:
builder.Services.AddCors(options =>
{
options.AddPolicy("RestrictOrigins", policy =>
{
policy.WithOrigins("https://mywebsite.com", "https://trustedpartner.com") // Allowed origins
.AllowAnyHeader()
.AllowAnyMethod();
});
});
// Apply the CORS policy
app.UseCors("RestrictOrigins");
Key Rules:
-
Avoid
AllowAnyOrigin
: Allowing all origins opens your API to vulnerabilities. -
Do not use
SetIsOriginAllowed(_ => true)
: This bypasses origin validation entirely. -
Limit methods and headers: Restrict
AllowAnyMethod
andAllowAnyHeader
to what is strictly necessary.
2. Implement Authentication and Authorization
Authentication ensures that only authorized users or systems can access your endpoints. A common approach is using JSON Web Tokens (JWT).
Steps to Implement JWT:
- On the client side, send the JWT in the request header:
Authorization: Bearer <your-jwt-token>
- On the server side, validate the token:
app.UseAuthentication();
app.UseAuthorization();
Example Configuration in ASP.NET Core:
builder.Services.AddAuthentication("Bearer")
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = "https://mywebsite.com",
ValidAudience = "https://mywebsite.com",
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("secret-key"))
};
});
3. Validate the Origin
Header Explicitly
Even with CORS configured, you can add an extra layer of security by manually validating the Origin
header in server-side middleware.
Example:
app.Use(async (context, next) =>
{
var origin = context.Request.Headers["Origin"].ToString();
var allowedOrigins = new[] { "https://mywebsite.com", "https://trustedpartner.com" };
if (!string.IsNullOrEmpty(origin) && !allowedOrigins.Contains(origin))
{
context.Response.StatusCode = StatusCodes.Status403Forbidden;
await context.Response.WriteAsync("Origin not allowed.");
return;
}
await next();
});
4. Block Suspicious IPs
Filter and block requests from known malicious IP addresses to reduce attack vectors.
Example Middleware:
app.Use(async (context, next) =>
{
var clientIp = context.Connection.RemoteIpAddress;
var blockedIps = new[] { "192.168.1.100", "10.0.0.50" };
if (blockedIps.Contains(clientIp.ToString()))
{
context.Response.StatusCode = StatusCodes.Status403Forbidden;
await context.Response.WriteAsync("Blocked IP.");
return;
}
await next();
});
5. Implement Rate Limiting
Protect your API from abuse and brute force attacks by limiting the number of requests a client can make.
Example with ASP.NET Core:
Install the package:
dotnet add package AspNetCoreRateLimit
Configure rate limiting:
builder.Services.AddMemoryCache();
builder.Services.Configure<IpRateLimitOptions>(options =>
{
options.GeneralRules = new List<RateLimitRule>
{
new RateLimitRule
{
Endpoint = "*",
Limit = 100, // Request limit
Period = "1m" // Per minute
}
};
});
builder.Services.AddInMemoryRateLimiting();
app.UseIpRateLimiting();
6. Use HTTPS for All Connections
Ensure secure communication between clients and your API by enforcing HTTPS.
Configure HTTPS in ASP.NET Core:
webBuilder.UseKestrel()
.UseHttps();
Redirect HTTP traffic to HTTPS:
app.UseHttpsRedirection();
7. Monitor and Log Requests
Implement logging to detect unusual patterns, such as multiple requests from unknown origins.
Example:
app.Use(async (context, next) =>
{
var origin = context.Request.Headers["Origin"].ToString();
Console.WriteLine($"Request from origin: {origin}");
await next();
});
Use tools like Application Insights, Serilog, or Elastic Stack for comprehensive monitoring.
8. Avoid Detailed Error Responses
Do not expose sensitive information in error messages, as it can aid attackers.
Example:
app.UseExceptionHandler("/error"); // Redirect errors to a secure page
Conclusion
Securing your API against unauthorized requests requires a multi-layered approach:
- Configure CORS properly.
- Validate origins and headers explicitly.
- Implement authentication and rate limiting.
- Use HTTPS and monitor traffic.
By following these best practices, you can significantly reduce the risk of unauthorized access and ensure that only trusted clients can interact with your API.
Top comments (4)
Excelente material mas gostaria de adicionar que configurar medidas de segurança, como rate limiting por exemplo, no proxy em vez do servidor de aplicação (como .Net no exemplo) oferece várias vantagens:
Melhor Performance: O proxy é otimizado para lidar com grandes volumes de requisições, aliviando a carga do servidor de aplicação.
Controle Centralizado: Permite gerenciar e aplicar regras de segurança de forma consistente em sistemas distribuídos.
Maior Segurança: Bloqueia tráfego excessivo ou malicioso antes que alcance o servidor, reduzindo vulnerabilidades.
Facilidade de Configuração: Ferramentas como
Nginx
oferecem módulos prontos, evitando a necessidade de escrever lógica personalizada.Políticas Avançadas: Suporte a limites dinâmicos, baseados em IP, cabeçalhos ou chaves de API, além de permitir "bursts" temporários.
Escalabilidade: Ajuda a manter o sistema eficiente ao adicionar novos servidores, sem necessidade de sincronizar estados.
Código Mais Limpo: A separação de responsabilidades mantém a lógica de segurança fora do código da aplicação.
Embora a aplicação também possa implementar medidas específicas, como limites baseados em usuários ou lógica de negócios, o uso de um proxy é mais eficiente, seguro e fácil de manter.
O uso do proxy é uma abordagem muito boa, mas devemos sempre prestar atenção natureza do projecto.
Se for um projecto que tem a margem que crescer em cada ano, a abordagem mais acertiva é usar proxy mas em sistema em ecossistemas pequeno, usar proxy acaba não batendo muito certo.
Muito obrigado pela contribuição, @marciopedrocomba !
Certo @tiagomabango mas vale lembrar que hoje em dia usar um proxy é muito barato e fora as questões de segurança você tem ganho de alto throughput, baixa latência, tens a questão de lidar com requisições em paralelo sem sobre carregar a tua aplicação, cache e muito mais. Então usar um proxy independente do tamanho do projecto é um must nos dias de hoje
@marciopedrocomba, um ponto muito importante!.
Obrigado, tomei notado...