DEV Community

Cover image for How to Secure Your API Against Unauthorized Requests
TiagoMabango
TiagoMabango

Posted on

9 1 1

How to Secure Your API Against Unauthorized Requests

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");
Enter fullscreen mode Exit fullscreen mode

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 and AllowAnyHeader 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:

  1. On the client side, send the JWT in the request header:
   Authorization: Bearer <your-jwt-token>
Enter fullscreen mode Exit fullscreen mode
  1. On the server side, validate the token:
   app.UseAuthentication();
   app.UseAuthorization();
Enter fullscreen mode Exit fullscreen mode

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"))
        };
    });
Enter fullscreen mode Exit fullscreen mode

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();
});
Enter fullscreen mode Exit fullscreen mode

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();
});
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode

Redirect HTTP traffic to HTTPS:

app.UseHttpsRedirection();
Enter fullscreen mode Exit fullscreen mode

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();
});
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Conclusion

Securing your API against unauthorized requests requires a multi-layered approach:

  1. Configure CORS properly.
  2. Validate origins and headers explicitly.
  3. Implement authentication and rate limiting.
  4. 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.

Image of Timescale

Timescale – the developer's data platform for modern apps, built on PostgreSQL

Timescale Cloud is PostgreSQL optimized for speed, scale, and performance. Over 3 million IoT, AI, crypto, and dev tool apps are powered by Timescale. Try it free today! No credit card required.

Try free

Top comments (12)

Collapse
 
marciopedrocomba profile image
marciopedrocomba • Edited

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.

Collapse
 
tiagomabango profile image
TiagoMabango

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 !

Collapse
 
marciopedrocomba profile image
marciopedrocomba • Edited

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

Thread Thread
 
tiagomabango profile image
TiagoMabango

@marciopedrocomba, um ponto muito importante!.

Obrigado, tomei notado...

Collapse
 
artydev profile image
artydev

Thank you very much, very instructfull article

Collapse
 
tiagomabango profile image
TiagoMabango

Thanks, is very good to know that!

Collapse
 
kwnaidoo profile image
Kevin Naidoo

Nice! This is a good start.

If I may make one suggestion. The thing with bad IPs is that they will DDOS your app by just making several requests in a short space of time. Each request will put pressure on your APP's runtime, so it's generally better to handle bad IPs at a firewall level.

So you would put a cloud firewall in front of your server first and block them there before they even get to the web/app server.

Additionally, you could add a blocker in web servers like Nginx. Nginx can further rate limit as well and requests to Nginx are less expensive compared to invoking your application runtime usually.

Collapse
 
tiagomabango profile image
TiagoMabango

In these cases, the proxy works very well, you just mentioned Nginx, which is very well known.

I noted the observation, thank you very much for your contribution.

Collapse
 
stevsharp profile image
Spyros Ponaris

Thanks for sharing!

Collapse
 
tiagomabango profile image
TiagoMabango

Thanks for your comment, It's great to know that we share and help others!!

Collapse
 
codethug profile image
Nicolas Quijano

I fail to see how this is tagged #c, which is for ANSI C programming language not C# !
Please change the tag to #csharp, muchas gracias
Cheers !

Collapse
 
tiagomabango profile image
TiagoMabango • Edited

thanks, i will change!

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Engage with a sea of insights in this enlightening article, highly esteemed within the encouraging DEV Community. Programmers of every skill level are invited to participate and enrich our shared knowledge.

A simple "thank you" can uplift someone's spirits. Express your appreciation in the comments section!

On DEV, sharing knowledge smooths our journey and strengthens our community bonds. Found this useful? A brief thank you to the author can mean a lot.

Okay