Como Desenvolver Aplicações Mais Seguras com C#
Guia prático de desenvolvimento seguro em C#: previna SQL injection, desserialização insegura, XXE, SSRF e riscos de cadeia de suprimentos no NuGet em .NET.
C# e a plataforma .NET impulsionam desde APIs corporativas até ferramentas de desktop e serviços cloud-native, e sua popularidade os torna um alvo natural para atacantes. A boa notícia é que o desenvolvimento seguro em C# é bastante alcançável: o runtime é seguro em termos de memória, o framework vem com fortes primitivas de segurança e a maioria das vulnerabilidades surge de padrões previsíveis e corrigíveis, e não de falhas profundas da linguagem. Este guia percorre os riscos mais relevantes para a segurança em .NET e as práticas concretas que os enfrentam, para que você possa entregar funcionalidades sem entregar pontos fracos.
A mentalidade que sustenta a codificação segura em C# é simples: trate toda entrada externa como não confiável, mantenha segredos fora do código e deixe o framework fazer o trabalho pesado em vez de criar suas próprias defesas do zero. Com essa base, as classes específicas de vulnerabilidade tornam-se muito mais fáceis de compreender.
SQL injection: parametrize tudo
O SQL injection continua sendo uma das falhas mais danosas e mais evitáveis. Ele ocorre quando a entrada do usuário é concatenada diretamente em uma string de consulta. Em C#, a correção é nunca montar SQL manualmente. Com ADO.NET, use comandos parametrizados: crie um SqlCommand e adicione valores por meio de command.Parameters.AddWithValue("@id", userId) em vez de interpolar userId no texto da consulta. Com o Entity Framework Core, as consultas LINQ são parametrizadas automaticamente e, quando você precisar de SQL bruto, use FromSqlInterpolated para que os valores interpolados sejam passados como parâmetros em vez de concatenados. Evite FromSqlRaw com entrada montada a partir de strings. A regra geral: se você consegue ver dados do usuário dentro de uma string de consulta, você tem um bug.
Desserialização insegura: aposente o BinaryFormatter
A desserialização transforma bytes de volta em objetos e, se esses bytes vêm de uma fonte não confiável, um atacante pode elaborar um payload que executa código ou corrompe o estado à medida que ele é reconstruído. O BinaryFormatter é o infrator clássico. Ele agora está obsoleto e desabilitado por padrão no .NET moderno, e a própria orientação da Microsoft é inequívoca de que ele não pode ser tornado seguro para dados não confiáveis. Remova-o completamente da sua base de código.
Para intercâmbio de dados, prefira o System.Text.Json, que não desserializa tipos arbitrários por padrão. O perigo retorna no momento em que você habilita o tratamento polimórfico ou com tipos embutidos. No Newtonsoft.Json, definir TypeNameHandling como qualquer coisa diferente de None em entrada não confiável reintroduz o mesmo risco de execução remota de código. Mantenha o tratamento de tipos desligado, desserialize para tipos DTO concretos que você controla e valide o resultado antes de usá-lo.
XXE em analisadores de XML
Ataques de XML External Entity (XXE) abusam de analisadores de XML que resolvem entidades externas, possibilitando a divulgação de arquivos ou SSRF. O .NET moderno é seguro por padrão. XmlReader e XDocument não resolvem entidades externas a menos que você opte por isso. O risco aparece quando código legado define um XmlResolver ou usa configurações mais antigas de DtdProcessing. Ao analisar XML não confiável, defina explicitamente DtdProcessing = DtdProcessing.Prohibit e deixe XmlResolver = null. Não reabilite a resolução de entidades para acomodar um formato de entrada conveniente.
SSRF e path traversal
O Server-Side Request Forgery (SSRF) ocorre quando sua aplicação faz uma requisição de saída com HttpClient para uma URL influenciada pela entrada do usuário, permitindo que um atacante alcance serviços internos ou endpoints de metadados da nuvem. Nunca passe uma URL bruta fornecida pelo usuário para o HttpClient. Valide contra uma allowlist de hosts e schemes permitidos, e resolva e verifique o destino antes de conectar. O path traversal é o equivalente no sistema de arquivos: uma entrada como ../../etc/passwd escapa de um diretório pretendido. Combine a entrada do usuário com um caminho base, chame Path.GetFullPath e confirme que o caminho resolvido ainda está sob o diretório que você espera antes de abrir o arquivo.
Mass assignment (over-posting) no ASP.NET Core
O model binding do ASP.NET Core mapeia campos da requisição para seus objetos automaticamente, o que é conveniente e, ocasionalmente, perigoso. Se você fizer o binding diretamente para uma entidade que tem uma propriedade IsAdmin ou Balance, um atacante pode definir campos que você nunca pretendeu expor, um padrão conhecido como over-posting ou mass assignment. A defesa é fazer o binding para view models ou DTOs criados para esse propósito, que contenham apenas os campos que um determinado endpoint deve aceitar, e então mapeá-los deliberadamente para suas entidades de domínio. Use listas [Bind] ou [FromBody] em tipos restritos em vez de expor seu modelo de dados diretamente.
Criptografia fraca
A criptografia falha silenciosamente. Evite algoritmos desatualizados como MD5, SHA-1 e DES. Para gerar hash de senhas, use um algoritmo lento e criado para esse fim, como o PBKDF2 via Rfc2898DeriveBytes, com uma alta contagem de iterações e um salt único por usuário, ou outra função adaptativa. Para hash geral, use SHA-256 ou melhor e, para criptografia simétrica, use AES com uma chave gerada de forma segura. Deixe o framework gerar aleatoriedade com o RandomNumberGenerator, nunca com o System.Random, para qualquer coisa sensível à segurança. Armazene chaves e segredos em um vault gerenciado ou provedor de configuração, não no código-fonte.
Risco de dependências e de cadeia de suprimentos (NuGet)
Uma aplicação .NET moderna é, em sua maior parte, código de outras pessoas. Um único pacote NuGet vulnerável ou malicioso pode comprometer toda a aplicação, então suas dependências pertencem claramente ao seu modelo de ameaças. Fixe versões, revise novos pacotes antes de adotá-los e execute dotnet list package --vulnerable --include-transitive para revelar problemas conhecidos, inclusive aqueles enterrados em dependências transitivas. Habilite arquivos de lock para restaurações reproduzíveis, fique atento a nomes de pacotes com typosquatting e remova dependências que você não usa mais. Saiba mais em nossa visão geral sobre análise de composição de software.
Ferramentas: torne a segurança automática
A diligência manual não escala, então incorpore essas verificações ao seu pipeline. O static application security testing (SAST) analisa seu código-fonte em C# em busca de injeção, desserialização insegura e uso incorreto de criptografia antes que o código seja mesclado. A software composition analysis (SCA) rastreia pacotes NuGet vulneráveis. A varredura de segredos captura credenciais antes que cheguem a um repositório. Executar os três no CI significa que cada commit é verificado da mesma forma, todas as vezes. Para a diferença entre análise estática e em tempo de execução, veja SAST vs DAST e, para o panorama mais amplo, nosso guia sobre desenvolvimento seguro de software e o OWASP Top 10, cuja edição de 2025 se relaciona de perto com os riscos acima.
Um checklist prático de C# seguro
- Use comandos parametrizados do ADO.NET e LINQ do EF Core ou
FromSqlInterpolated; nunca concatene SQL. - Remova o
BinaryFormatter; mantenha o tratamento de tipos em JSON desligado para entrada não confiável e desserialize para DTOs concretos. - Proíba o processamento de DTD e anule o
XmlResolverao analisar XML não confiável. - Valide URLs de saída contra uma allowlist e canonicalize caminhos de arquivo para bloquear SSRF e traversal.
- Faça o binding para view models, não para entidades, para evitar over-posting.
- Use criptografia moderna e o
RandomNumberGenerator; mantenha os segredos em um vault. - Audite continuamente as dependências NuGet e imponha SAST, SCA e varredura de segredos no CI.
Como a Rainforest ajuda
A Rainforest traz o teste de segurança de aplicações para o fluxo de trabalho que sua equipe já usa. Ela analisa seu código C# e .NET em busca dos padrões de vulnerabilidade acima, sinaliza dependências NuGet vulneráveis e captura segredos expostos, tudo integrado ao seu pipeline de CI/CD para que as descobertas apareçam no momento do commit, com o contexto necessário para corrigi-las rapidamente. Em vez de uma revisão de segurança que acontece tarde e atrasa todo mundo, você obtém um feedback contínuo e amigável para o desenvolvedor, que mantém o risco fora de produção.
Pronto para vê-la na sua própria base de código? Agende uma demonstração e mostraremos como a Rainforest se encaixa na sua stack .NET.
Perguntas frequentes
C# é seguro?
C# e .NET são seguros em termos de memória e vêm com fortes padrões de segurança, então a linguagem em si é uma base sólida. O risco no mundo real vem de como as aplicações lidam com entrada não confiável, gerenciam segredos, analisam dados e incorporam dependências. Siga práticas de codificação segura e C# pode ser muito seguro.
Quais são as vulnerabilidades de segurança comuns em C#/.NET?
Os problemas sérios mais frequentes são SQL injection a partir de consultas montadas com strings, desserialização insegura, XXE em analisadores de XML mal configurados, SSRF e path traversal a partir de entrada não validada, mass assignment no model binding do ASP.NET Core, criptografia fraca e dependências NuGet vulneráveis.
Por que o BinaryFormatter é perigoso?
O BinaryFormatter reconstrói grafos de objetos arbitrários a partir de bytes serializados, então um payload elaborado pode disparar a execução remota de código durante a desserialização. Ele não pode ser tornado seguro para dados não confiáveis, agora está obsoleto e é desabilitado por padrão no .NET moderno. Substitua-o pelo System.Text.Json e tipos DTO concretos.
Como gerencio o risco de dependências NuGet?
Trate as dependências como parte da sua superfície de ataque. Fixe versões, avalie novos pacotes, habilite arquivos de lock, remova os não utilizados e execute dotnet list package --vulnerable --include-transitive regularmente. Automatize isso com software composition analysis no CI, para que vulnerabilidades conhecidas sejam capturadas em cada build.
Como faço a varredura de vulnerabilidades no código C#?
Use o static application security testing (SAST) para analisar o código-fonte em busca de padrões inseguros, a software composition analysis (SCA) para pacotes NuGet vulneráveis e a varredura de segredos para credenciais expostas. Executar essas verificações no seu pipeline de CI/CD, como a Rainforest faz, checa cada commit automaticamente.

Escrito por
Bruno Baldo
CMO
Um pouco de marketing e um pouco de curiosidade e temos a receita pra criar um apaixonado por cyber!

Desenvolvimento seguro de software: práticas para um SDLC seguro
Guia prático de desenvolvimento seguro: SDLC seguro fase a fase, codificação segura, shift-left, DevSecOps, LGPD e automação no CI/CD.

SAST vs DAST: as diferenças e quando usar cada um
SAST vs DAST: como os testes estático e dinâmico de segurança de aplicações diferem, quando usar cada um e por que os dois se complementam.
