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.
| 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 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 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(); 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> 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 }); }); ✅ 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); 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")); E aplique a política:
app.MapDelete("/api/orders/{id:int}", (int id) => { return Results.NoContent(); }) .RequireAuthorization("AdminOnly"); 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 } 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); 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(); 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 E registre os serviços:
builder.Services.AddValidation(); 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; 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; }); }); Ative o middleware:
app.UseRateLimiter(); Aplique a política:
app.MapGet("/api/products", () => { return Results.Ok(new[] { "Notebook", "Mouse", "Keyboard" }); }) .RequireAuthorization() .RequireRateLimiting("api"); 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 ⚠️ 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); }); 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); }); 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(); 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"); }); }); Ative a política:
app.UseCors("Frontend"); 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 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"); } 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); Evite:
// Não registre tokens, senhas ou dados de cartão. logger.LogInformation( "Authorization: {Token}", accessToken); Padronize os erros com Problem Details:
builder.Services.AddProblemDetails(); E habilite o middleware:
app.UseExceptionHandler(); app.UseStatusCodePages(); 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/ 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:


