DEV Community

Cover image for 🚀 .NET 10 + OWASP: como implementar regras de segurança em uma API moderna
Danilo O. Pinheiro, dopme.io
Danilo O. Pinheiro, dopme.io

Posted on

🚀 .NET 10 + OWASP: como implementar regras de segurança em uma API moderna

Guia prático de segurança com ASP.NET Core, autenticação, autorização, validação, rate limiting e proteção contra vulnerabilidades
Em uma arquitetura moderna baseada em APIs REST, implementar endpoints funcionais não é suficiente. Precisamos garantir que cada requisição seja autenticada, autorizada, validada e monitorada, reduzindo os riscos de exposição de dados e exploração de vulnerabilidades.
O OWASP API Security Top 10 reúne dez categorias importantes de riscos específicos de APIs, incluindo falhas de autorização, autenticação inadequada, consumo irrestrito de recursos, configurações inseguras e consumo inseguro de serviços externos.

OWASP API Security Top 10

Neste artigo, vamos demonstrar como aplicar esses princípios em uma API desenvolvida com .NET 10 e ASP.NET Core, utilizando código C#, políticas de segurança e boas práticas que podem ser adaptadas a aplicações corporativas e arquiteturas de microserviços.
O objetivo é construir uma base reutilizável para projetos que precisam evoluir com segurança desde o início.
Autenticação e autorização
Identificar o usuário e controlar o acesso aos recursos e operações.
Proteção contra abuso
Limitar requisições, validar entradas e reduzir o consumo excessivo de recursos.
Monitoramento e auditoria
Registrar eventos relevantes sem expor senhas, tokens ou dados sensíveis.

🧭 1. Entendendo o OWASP API Security Top 10

A edição de referência utilizada neste artigo é a OWASP API Security Top 10 — 2023. Ela organiza os principais riscos específicos de APIs da seguinte maneira.

image

Categoria Risco Exemplo de proteção
API1 Falha de autorização em nível de objeto Verificar se o usuário pode acessar aquele pedido
API2 Falha de autenticação Validar tokens e configurar corretamente o provedor de identidade
API3 Falha de autorização em propriedades Controlar quais campos podem ser lidos ou alterados
API4 Consumo irrestrito de recursos Rate limiting, paginação e limites de payload
API5 Falha de autorização em nível de função Restringir operações administrativas
API6 Acesso irrestrito a fluxos de negócio sensíveis Limitar tentativas e automatização de operações críticas
API7 Server-Side Request Forgery (SSRF) Restringir destinos de requisições externas
API8 Configuração insegura Revisar CORS, HTTPS, segredos e ambientes
API9 Gerenciamento inadequado de inventário Controlar endpoints e versões publicadas
API10 Consumo inseguro de APIs Validar respostas e tratar falhas de terceiros

A regra principal é que segurança não deve depender de uma única biblioteca ou middleware. É necessário combinar controles de infraestrutura, aplicação, identidade e regras de negócio.

🏗️ 2. Criando o projeto .NET 10

Vamos desenvolver uma API de pedidos para demonstrar os controles de segurança. O exemplo será baseado em Minimal APIs, permitindo concentrar a implementação em poucos arquivos.

Crie o projeto:

dotnet new webapi -n SecureOrdersApi
cd SecureOrdersApi
Enter fullscreen mode Exit fullscreen mode

Para esse cenário, utilizaremos:

  • ASP.NET Core: endpoints e pipeline HTTP.
  • JWT Bearer: validação de tokens emitidos por um provedor de identidade.
  • Authorization Policies: regras de acesso.
  • Data Annotations: validação dos dados recebidos.
  • Rate Limiting: controle de volume de requisições.
  • Problem Details: respostas de erro padronizadas.
  • ILogger: registro de eventos operacionais.

O ASP.NET Core oferece recursos nativos de autenticação, autorização, HTTPS, proteção de dados e outros controles de segurança.

🔐 3. Implementando autenticação com JWT

O primeiro controle é verificar a identidade de quem acessa a API.

Instale o pacote de autenticação:

dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
Enter fullscreen mode Exit fullscreen mode

Um token JWT deve ser emitido por um provedor de identidade confiável, como um serviço compatível com OAuth 2.0 e OpenID Connect. A API deve validar assinatura, emissor, audiência e expiração do token.

Configure a autenticação no Program.cs:

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;

var builder = WebApplication.CreateBuilder(args);

var authority = builder.Configuration["Authentication:Authority"]
    ?? throw new InvalidOperationException(
        "Authentication:Authority não configurado.");

var audience = builder.Configuration["Authentication:Audience"]
    ?? throw new InvalidOperationException(
        "Authentication:Audience não configurado.");

builder.Services
    .AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.Authority = authority;
        options.Audience = audience;
    });

builder.Services.AddAuthorizationBuilder()
    .SetFallbackPolicy(
        new AuthorizationPolicyBuilder()
            .RequireAuthenticatedUser()
            .Build());

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthentication();
app.UseAuthorization();

app.MapGet("/api/private", () =>
    Results.Ok(new { message = "Acesso autorizado." }));

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

Configure Authentication:Authority e Authentication:Audience com os valores reais do seu provedor de identidade, por exemplo por meio de variáveis de ambiente.

A política de fallback exige autenticação para endpoints que não tenham uma política de autorização explícita. Endpoints públicos devem ser marcados conscientemente com .AllowAnonymous().

🧪 Resultado esperado

Cenário Resposta
Sem token em endpoint protegido 401 Unauthorized
Token inválido ou expirado 401 Unauthorized
Token válido e acesso permitido 200 OK
Usuário autenticado sem permissão 403 Forbidden

Autenticação não significa autorização: saber quem é o usuário não garante que ele possa acessar qualquer recurso.

🛡️ 4. API1 e API5 — protegendo recursos e funções

Um dos erros mais comuns em APIs é permitir que um usuário autenticado consulte dados de outra pessoa apenas alterando o identificador enviado na URL.

Imagine a seguinte requisição:

GET /api/orders/200
Authorization: Bearer <access_token>
Enter fullscreen mode Exit fullscreen mode

O fato de o usuário possuir um token válido não significa que ele seja proprietário do pedido 200.

❌ Implementação vulnerável

app.MapGet("/api/orders/{id:int}", (int id) =>
{
    // Vulnerável: não verifica a propriedade do pedido.
    return Results.Ok(new { Id = id });
});
Enter fullscreen mode Exit fullscreen mode

✅ Implementação com autorização por recurso

Para demonstrar a regra, vamos utilizar uma lista em memória. Em produção, a consulta deve ser feita pelo repositório ou banco de dados.

var orders = new[]
{
    new Order(100, "user-123", "Paid"),
    new Order(200, "user-456", "Pending")
};

app.MapGet("/api/orders/{id:int}", (
    int id,
    HttpContext context) =>
{
    var subject = context.User.FindFirst("sub")?.Value;

    if (string.IsNullOrWhiteSpace(subject))
        return Results.Forbid();

    var order = orders.FirstOrDefault(x => x.Id == id);

    if (order is null || order.OwnerId != subject)
        return Results.NotFound();

    return Results.Ok(new
    {
        order.Id,
        order.Status
    });
}).RequireAuthorization();

record Order(int Id, string OwnerId, string Status);
Enter fullscreen mode Exit fullscreen mode

Nesse exemplo, a API retorna 404 Not Found quando o pedido não existe ou não pertence ao usuário, evitando revelar a existência de recursos alheios.

Importante: o exemplo pressupõe que o provedor de identidade emite uma claim sub estável e confiável. Em sistemas reais, a associação entre identidade e proprietário deve ser feita com base no identificador validado pelo servidor.

A OWASP recomenda verificar a autorização em todas as operações que acessam dados utilizando identificadores fornecidos pelo cliente.

🔑 Protegendo funções administrativas

Para restringir operações administrativas, utilize políticas específicas:

builder.Services.AddAuthorizationBuilder()
    .AddPolicy("AdminOnly", policy =>
        policy.RequireRole("Admin"));
Enter fullscreen mode Exit fullscreen mode

E aplique a política:

app.MapDelete("/api/orders/{id:int}", (int id) =>
{
    return Results.NoContent();
})
.RequireAuthorization("AdminOnly");
Enter fullscreen mode Exit fullscreen mode

Essa regra demonstra o controle de acesso por função. Para autorização em nível de objeto, a verificação de propriedade ou permissão específica continua necessária.

📦 5. API3 — evitando exposição excessiva e Mass Assignment

Outro risco é aceitar diretamente entidades de domínio ou permitir que o cliente altere propriedades que deveriam ser controladas exclusivamente pelo servidor.

Imagine que o cliente envie:

{
  "productName": "Notebook",
  "quantity": 2,
  "isAdmin": true,
  "discount": 100
}
Enter fullscreen mode Exit fullscreen mode

Se a API aceitar indiscriminadamente todas as propriedades, o cliente poderá tentar modificar campos que não deveria controlar.

✅ Utilize DTOs específicos

using System.ComponentModel.DataAnnotations;

public sealed record CreateOrderRequest(
    [Required, StringLength(100, MinimumLength = 2)]
    string ProductName,

    [Range(1, 20)]
    int Quantity);
Enter fullscreen mode Exit fullscreen mode

No endpoint, aceite somente os campos permitidos:

app.MapPost("/api/orders", (
    CreateOrderRequest request) =>
{
    var order = new
    {
        Id = Guid.NewGuid(),
        request.ProductName,
        request.Quantity,
        Status = "Pending"
    };

    return Results.Created(
        $"/api/orders/{order.Id}", order);
})
.RequireAuthorization();
Enter fullscreen mode Exit fullscreen mode

A validação deve estar efetivamente habilitada e aplicada ao modelo recebido. No .NET 10, Minimal APIs oferecem suporte integrado a validação com AddValidation() e atributos como [Required] e [Range].

Para ativá-la no projeto, instale o pacote de validação, caso necessário:

dotnet add package Microsoft.Extensions.Validation
Enter fullscreen mode Exit fullscreen mode

E registre os serviços:

builder.Services.AddValidation();
Enter fullscreen mode Exit fullscreen mode

Além disso, em uma aplicação real, o serviço de criação precisa verificar estoque, preço, permissões e regras de negócio. Os valores financeiros devem ser calculados no servidor, nunca confiados ao cliente.

🚦 6. API4 — implementando Rate Limiting

Uma API sem controle de volume pode sofrer consumo excessivo de recursos, tentativas automatizadas ou aumento inesperado de custos.

O ASP.NET Core disponibiliza middleware de rate limiting com políticas globais e específicas por endpoint.

Adicione os namespaces:

using System.Threading.RateLimiting;
Enter fullscreen mode Exit fullscreen mode

Configure uma política de limitação:

builder.Services.AddRateLimiter(options =>
{
    options.RejectionStatusCode = 429;

    options.AddFixedWindowLimiter("api", limiter =>
    {
        limiter.PermitLimit = 60;
        limiter.Window = TimeSpan.FromMinutes(1);
        limiter.QueueLimit = 0;
        limiter.AutoReplenishment = true;
    });
});
Enter fullscreen mode Exit fullscreen mode

Ative o middleware:

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

Aplique a política:

app.MapGet("/api/products", () =>
{
    return Results.Ok(new[]
    {
        "Notebook",
        "Mouse",
        "Keyboard"
    });
})
.RequireAuthorization()
.RequireRateLimiting("api");
Enter fullscreen mode Exit fullscreen mode

O exemplo permite até 60 requisições por janela de um minuto, conforme a política configurada.

Quando o limite for excedido, a resposta será:

HTTP/1.1 429 Too Many Requests
Enter fullscreen mode Exit fullscreen mode

⚠️ Cuidados em produção

  • Ajuste os limites conforme o perfil de consumo real.
  • Considere políticas por identidade autenticada ou cliente.
  • Não confie cegamente em cabeçalhos de IP enviados pelo cliente.
  • Em múltiplas instâncias, avalie uma estratégia distribuída ou um gateway com limites compartilhados.
  • Utilize proteção de infraestrutura contra DDoS quando necessário.

Rate limiting na aplicação é uma camada de defesa, não um substituto para proteção de rede.

🔍 7. API7 — prevenção contra SSRF

Server-Side Request Forgery ocorre quando a API é induzida a fazer requisições para destinos inesperados, por exemplo serviços internos ou endpoints de metadados da infraestrutura.

❌ Evite aceitar qualquer URL do cliente

// Não implemente dessa forma em produção.
app.MapGet("/api/fetch", async (
    string url,
    HttpClient client) =>
{
    return await client.GetStringAsync(url);
});
Enter fullscreen mode Exit fullscreen mode

Essa abordagem permite que o usuário controle o destino da requisição.

✅ Prefira destinos conhecidos e controlados

builder.Services.AddHttpClient("Catalog", client =>
{
    client.BaseAddress =
        new Uri("https://catalog.example.com/");

    client.Timeout = TimeSpan.FromSeconds(5);
});
Enter fullscreen mode Exit fullscreen mode

Utilize o cliente configurado por meio de IHttpClientFactory e aceite identificadores ou caminhos validados, em vez de URLs arbitrárias.

Para integrações que realmente exigem destinos dinâmicos, implemente uma allowlist de hosts, valide esquema e porta, controle redirecionamentos e bloqueie destinos internos e endereços de metadados. A validação de DNS deve considerar mudanças de resolução e técnicas de DNS rebinding.

A OWASP identifica SSRF como um dos riscos específicos de segurança de APIs.

⚙️ 8. API8 — configuração segura do ASP.NET Core

Uma API pode conter código correto e ainda ser vulnerável por causa de configurações inadequadas.

🔒 HTTPS

Mantenha o redirecionamento para HTTPS:

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

Em produção, configure corretamente certificados TLS e o proxy reverso. A terminação TLS e os cabeçalhos encaminhados devem ser configurados conforme a infraestrutura confiável.

🌐 CORS

Configure explicitamente os origens autorizados quando a API for consumida por aplicações web em outros domínios:

builder.Services.AddCors(options =>
{
    options.AddPolicy("Frontend", policy =>
    {
        policy
            .WithOrigins("https://app.example.com")
            .WithMethods("GET", "POST", "PUT", "DELETE")
            .WithHeaders("Authorization", "Content-Type");
    });
});
Enter fullscreen mode Exit fullscreen mode

Ative a política:

app.UseCors("Frontend");
Enter fullscreen mode Exit fullscreen mode

CORS controla quais origens de navegador podem ler respostas; não é autenticação e não bloqueia, por si só, chamadas feitas por clientes fora do navegador.

🔑 Segredos e configurações

  • Não armazene senhas, chaves privadas ou tokens no repositório.
  • Utilize um gerenciador de segredos em produção.
  • Mantenha ambientes de desenvolvimento e produção separados.
  • Desative páginas de erro detalhadas e ferramentas de diagnóstico públicas em produção.
  • Restrinja as permissões de acesso ao banco de dados e aos serviços externos.

A documentação oficial do ASP.NET Core reúne orientações sobre HTTPS, autenticação, autorização, CORS e gerenciamento de segredos.

🧾 9. API9 — inventário e versionamento de APIs

Em sistemas corporativos, endpoints antigos, rotas administrativas esquecidas e versões ainda publicadas podem aumentar a superfície de ataque.

Algumas medidas importantes:

  • Documentar os endpoints públicos e internos.
  • Definir um processo de descontinuação de versões.
  • Remover rotas de diagnóstico que não são necessárias em produção.
  • Controlar a publicação de documentação OpenAPI.
  • Revisar quais APIs estão acessíveis externamente.
  • Monitorar versões e dependências fora de suporte.

Por exemplo, uma API pode organizar suas rotas assim:

/api/v1/orders
/api/v1/products
/api/v2/orders
Enter fullscreen mode Exit fullscreen mode

A simples presença de /v1 ou /v2 não implementa versionamento completo: é preciso controlar contratos, compatibilidade, autenticação e o ciclo de vida de cada versão.

A OWASP recomenda manter um inventário atualizado de hosts, endpoints e versões das APIs.

🔗 10. API10 — consumo seguro de APIs externas

Uma resposta de terceiro também deve ser considerada entrada não confiável.

Ao integrar APIs externas:

  • Valide os campos e os tipos retornados.
  • Defina timeouts.
  • Limite o tamanho das respostas.
  • Trate erros sem expor detalhes internos.
  • Configure retries com cuidado para não multiplicar chamadas.
  • Evite encaminhar credenciais para destinos não autorizados.
  • Verifique a autenticidade e a integridade dos dados conforme o protocolo utilizado.

Por exemplo:

var response = await client.GetAsync(
    "products",
    cancellationToken);

if (!response.IsSuccessStatusCode)
{
    logger.LogWarning(
        "Consulta ao catálogo falhou. Status: {StatusCode}",
        (int)response.StatusCode);

    return Results.Problem(
        statusCode: StatusCodes.Status502BadGateway,
        title: "Serviço externo indisponível");
}
Enter fullscreen mode Exit fullscreen mode

O client e o logger devem ser injetados pelo sistema de dependências. O exemplo pressupõe que a requisição e o conteúdo já possuem limites adequados; em produção, também valide o JSON recebido antes de utilizar seus dados.

A categoria API10 da OWASP destaca justamente os riscos de confiar excessivamente em serviços e respostas de terceiros.

📝 11. Logs seguros e tratamento de erros

Logs são importantes para identificar ataques, investigar falhas e entender o comportamento da API. Porém, registrar dados sensíveis pode criar uma nova vulnerabilidade.

Prefira:

logger.LogWarning(
    "Acesso negado ao pedido {OrderId}",
    orderId);
Enter fullscreen mode Exit fullscreen mode

Evite:

// Não registre tokens, senhas ou dados de cartão.
logger.LogInformation(
    "Authorization: {Token}",
    accessToken);
Enter fullscreen mode Exit fullscreen mode

Padronize os erros com Problem Details:

builder.Services.AddProblemDetails();
Enter fullscreen mode Exit fullscreen mode

E habilite o middleware:

app.UseExceptionHandler();
app.UseStatusCodePages();
Enter fullscreen mode Exit fullscreen mode

Configure também logging estruturado, correlação de requisições e alertas para eventos relevantes. O objetivo é permitir investigação sem revelar stack traces, informações de infraestrutura ou dados pessoais ao consumidor da API.

🧪 12. Testando as regras de segurança

Uma política de segurança só é útil se pudermos verificar seu comportamento.

Monte testes automatizados para cenários como:

Teste Resultado esperado
Endpoint privado sem token 401
Token inválido 401
Usuário tentando acessar pedido alheio 404 ou 403, conforme a política
Usuário comum tentando executar função administrativa 403
Payload com valores inválidos 400
Requisições acima do limite 429
Serviço externo indisponível Erro controlado, sem detalhes internos
Endpoint descontinuado Não deve permanecer exposto sem necessidade

Além dos testes de unidade e integração, considere análise estática, verificação de dependências, testes de segurança dinâmicos e revisão de configurações no pipeline CI/CD.

Os testes devem usar identidades e tokens de teste controlados. Nunca execute testes destrutivos contra sistemas sem autorização.

🏛️ 13. Como organizar as regras em uma arquitetura .NET

Para projetos maiores, evite concentrar toda a segurança em um único Program.cs.

Uma estrutura possível:

SecureOrdersApi/
├── src/
│   ├── SecureOrdersApi.Api/
│   │   ├── Endpoints/
│   │   ├── Middleware/
│   │   ├── Security/
│   │   └── Program.cs
│   ├── SecureOrdersApi.Application/
│   │   ├── Orders/
│   │   ├── Validators/
│   │   └── Authorization/
│   ├── SecureOrdersApi.Domain/
│   │   ├── Entities/
│   │   └── Rules/
│   └── SecureOrdersApi.Infrastructure/
│       ├── Persistence/
│       └── Integrations/
└── tests/
    ├── UnitTests/
    ├── IntegrationTests/
    └── SecurityTests/
Enter fullscreen mode Exit fullscreen mode

A API deve aplicar os controles HTTP, a camada Application deve orquestrar os casos de uso e a camada Domain deve preservar as regras de negócio. A Infrastructure implementa persistência e integrações.

Essa separação facilita testes, revisão de segurança e manutenção sem confundir autenticação, autorização e validação de negócio.

✅ 14. Checklist de segurança para produção

Checklist OWASP + .NET 10

0 de 10

Validar assinatura, emissor, audiência e expiração dos tokens

Exigir autenticação por padrão nos endpoints privados

Verificar autorização por recurso e por função

Utilizar DTOs e validar entradas

Aplicar rate limiting e limites de payload

Configurar HTTPS, CORS e segredos corretamente

Restringir URLs e destinos de integrações externas

Padronizar erros e não expor informações internas

Manter inventário de endpoints e versões

Automatizar testes de segurança no CI/CD

Limpar checklist

Esse checklist é um ponto de partida, não uma certificação de conformidade. Os controles devem ser avaliados conforme a arquitetura, os dados processados, as ameaças e os requisitos regulatórios de cada sistema.

🎯 Conclusão

Implementar as regras da OWASP em uma API com .NET 10 significa incorporar segurança ao ciclo de desenvolvimento, e não apenas adicionar um middleware no final do projeto.

Com autenticação JWT corretamente configurada, autorização por recurso e função, validação de entradas, rate limiting, tratamento seguro de erros e controle das integrações, criamos uma base muito mais resistente a falhas comuns de APIs.

O passo seguinte é automatizar os testes, revisar as configurações de infraestrutura e incluir verificações de segurança no pipeline de entrega.

Uma API moderna não é apenas aquela que responde rápido. É aquela que responde corretamente, respeita as permissões e protege os dados em todas as etapas.

🤝 Conecte-se Comigo

Se você trabalha com .NET moderno e quer evoluir em arquitetura, C#, segurança de APIs, DevOps ou interoperabilidade, vamos conversar:

📚 Referências oficiais

Top comments (0)