Skip to main content

Como evitar a injeção de SQL em C# com o Entity Framework

Escrito por
feature security

30 de julho de 2024

0 minutos de leitura

A importância de evitar a injeção de SQL

A injeção de SQL (SQLi) é uma das vulnerabilidades de segurança mais graves em aplicações web. Ela ocorre quando um invasor consegue manipular as consultas SQL executadas por uma aplicação ao inserir código SQL malicioso em campos de entrada do usuário. A SQLi pode levar ao acesso não autorizado a dados confidenciais, à corrupção de dados ou até mesmo ao controle total do servidor de banco de dados. Evitar a SQLi é essencial para manter a integridade, a confidencialidade e a disponibilidade dos dados, além da segurança geral da aplicação.

Um dos erros mais comuns cometidos por desenvolvedores é usar concatenação de strings para criar consultas SQL. Nessa abordagem, os parâmetros fornecidos pelo usuário fazem parte da consulta SQL e podem influenciar o fluxo de execução ou uma consulta. Veja um exemplo de código C# vulnerável:

public List SearchProduct(string search)
{
    var query = $"SELECT * FROM Product WHERE Name LIKE '%{search}%' OR Description LIKE '%{search}%'";
    return context.Users.FromSqlRaw(query).ToList();
}

Ao usar caracteres como ; e --, podemos influenciar o fluxo de execução da consulta, encerrando-a antes do previsto (;) e definindo o restante da string como comentário (--).

Se o nome for definido como Brian'; DROP TABLE Users;-- , a consulta ficará assim:

SELECT * FROM Product WHERE Name LIKE = '%Brian'; DROP TABLE Users; --' OR Description LIKE '%{search}%'";

A consulta inicial é interrompida após o ;. Em seguida, uma nova consulta exclui a tabela Users, e o restante da consulta original é tratado como comentário. Da mesma forma, é possível modificar ou inserir novos dados no banco de dados quando as consultas SQL são implementadas dessa maneira.

Escape de caracteres versus instruções preparadas

O segredo é separar os parâmetros de entrada da consulta propriamente dita, para que os dados fornecidos pelo usuário não possam mais influenciar o fluxo de execução. Muitas linguagens oferecem funções que permitem criar consultas parametrizadas e, assim, separar a entrada do usuário da consulta em si.

A questão é como isso é implementado nos bastidores, já que há basicamente duas maneiras de fazer isso: usar escape de caracteres ou instruções preparadas.

Escape de caracteres

O escape de caracteres é o processo de sanitizar as entradas do usuário adicionando caracteres de escape antes de caracteres potencialmente perigosos, como aspas. No entanto, esse processo está sujeito a erros e não é infalível. O escape é feito no lado do cliente (a aplicação), e a consulta com os caracteres escapados é enviada ao banco de dados como uma única instrução. Ele exige cuidado para lidar com diversos casos extremos e, em geral, não é recomendado como principal defesa contra SQLi.

Instruções preparadas

As instruções preparadas com consultas SQL parametrizadas funcionam de outra maneira. A estrutura SQL é definida separadamente e enviada ao servidor do banco de dados em uma primeira etapa. O banco de dados pode criar o plano de execução antes de receber os parâmetros. Em uma segunda etapa, os parâmetros são enviados ao banco de dados. Como o plano de execução já foi criado, os parâmetros são sempre tratados como dados, não como código executável, e não podem influenciar a execução.

Como usar o Entity Framework para evitar a injeção de SQL

O Entity Framework (EF) oferece vários métodos para interagir com o banco de dados com segurança, sem expor a aplicação a vulnerabilidades de SQLi. Entre eles estão as consultas LINQ, FromSqlInterpolated e FromSqlRaw com parâmetros explícitos.

Usando LINQ

O Language Integrated Query (LINQ) oferece uma maneira segura de interagir com bancos de dados. Essa é a abordagem recomendada para a maioria das consultas ao banco de dados no EF. O LINQ oferece um alto nível de abstração, permitindo que você escreva consultas diretamente em C# sem precisar conhecer a sintaxe SQL. Como ele é integrado ao C#, você também conta com a verificação de sintaxe em tempo de compilação.
O Entity Framework converte automaticamente as consultas LINQ em SQL usando instruções preparadas e parametrização, o que evita a injeção de SQL.

public SearchProduct List(string search)
{
   return dbContext.Products
       .Where(p => p.Name.Contains(search) ||
                   p.Description.Contains(search))
       .ToList();
}

Embora o LINQ seja muito prático e seguro, às vezes ele pode gerar SQL menos eficiente do que consultas SQL escritas manualmente, principalmente em consultas complexas. A camada de tradução pode gerar sobrecarga de desempenho. Além disso, o uso do LINQ é limitado pela capacidade do provedor do EF de converter código C# em SQL. Algumas funcionalidades complexas do SQL, como certos tipos de junções, subconsultas ou funções de janela, podem ser difíceis ou impossíveis de expressar em LINQ.

Usando FromSqlInterpolated

FromSqlInterpolated é outra maneira segura de executar consultas SQL brutas. Ele usa interpolação de strings e gerencia a parametrização automaticamente, também com instruções preparadas.

public List<User> GetUserByName(string name)
{
 	return context.Users
.FromSqlInterpolated($"SELECT * FROM Users WHERE Name = {name}")
.ToList();
}

Com FromSqlInterpolated, o EF garante que a variável name seja parametrizada com segurança, protegendo contra a injeção de SQL. O Entity Framework Core analisa a string interpolada e identifica as expressões interpoladas. Em seguida, ele substitui essas expressões por espaços reservados para parâmetros no comando SQL. Os parâmetros SQL correspondentes são criados, e os valores das expressões interpoladas são atribuídos a esses parâmetros.

Nos bastidores, a função FromSqlInterpolated usa uma instrução preparada com consultas parametrizadas. Por isso, ela é segura contra a injeção de SQL. Quando você precisa fazer consultas mais complexas, que são mais fáceis de expressar em SQL do que na sintaxe LINQ, FromSqlInterpolated é uma ótima alternativa.

Usando FromSqlRaw com parâmetros explícitos

No exemplo inicial, criamos primeiro a consulta e a passamos inteira para a instrução FromSQLRaw. No entanto, é possível usar FromSqlRaw com segurança. Ao usar parâmetros explícitos com FromSqlRaw, o EF garante que a entrada do usuário seja parametrizada corretamente, evitando a injeção de SQL. Esse método consiste em definir consultas SQL com espaços reservados para parâmetros e fornecer os valores separadamente, garantindo que as entradas do usuário sejam tratadas como dados, e não como código executável.

public List<Product> SearchProduct(string search) 
{ 
return context.Products
.FromSqlRaw( 
"SELECT * FROM Product WHERE Name LIKE {0} OR Description LIKE {0}", "%" + search + "%" ).ToList(); 
}

Nesta versão, o espaço reservado {0} é usado na string da consulta SQL, e o valor do parâmetro é passado diretamente como argumento para FromSqlRaw. Isso garante que a entrada do usuário seja parametrizada com segurança, protegendo contra a injeção de SQL.

Encontrar, corrigir e prevenir

A diferença entre usar FromSqlRaw com segurança ou não é muito pequena e pode passar despercebida. Por isso, todo desenvolvedor deve usar ferramentas como o Snyk Code para receber alertas sobre construções de código inseguras em C#.

Tela do Snyk Code destaca uma entrada HTTP não sanitizada que chega a FromSqlRaw e identifica uma vulnerabilidade de injeção de SQL em TasksController.cs.

No exemplo acima, um problema semelhante de injeção de SQL é identificado por meio da integração com o Git. Verificar esse tipo de problema durante o ciclo de desenvolvimento é essencial para quem prioriza a segurança sem abrir mão da velocidade.

No entanto, também é fundamental conhecer bem os frameworks usados e investir em capacitação. Ao trabalhar com EF, use a sintaxe LINQ por padrão. O LINQ deve ser suficiente, a menos que você precise de desempenho máximo ou esteja criando consultas muito complexas. Para consultas SQL muito específicas e complexas, use a abordagem FromSqlInterpolated descrita acima. Meu conselho é evitar FromSqlRaw. Embora seja possível usá-lo com segurança, a diferença entre uma abordagem correta e uma incorreta é sutil e pode passar despercebida.

Resumindo, veja uma lista fácil de consultar. Ao usar o Entity Framework:

  • Use a sintaxe LINQ para interagir com o banco de dados e evite SQL.

  • Use FromSQLInterpolated apenas em situações complexas.

  • Use uma ferramenta de análise de código, como o Snyk Code, para ajudar a detectar erros.

Agora que você já sabe um pouco mais, está pronto para usar o Entity Framework em C# e proteger seu código contra vulnerabilidades de injeção de SQL. Para facilitar esse processo, crie uma conta gratuita na Snyk ou agende uma demonstração hoje mesmo e descubra como a Snyk protege seu código enquanto você o escreve.

Comece a jogar Capture the Flag

Aprenda a resolver desafios de Capture the Flag assistindo à gravação sob demanda do nosso workshop virtual introdutório.

Leia mais

feature insights context
Blog

Os ataques autônomos já chegaram. A defesa precisa acompanhar o ritmo.

Os atacantes autônomos estão reduzindo o tempo disponível para a defesa. Saiba como a descoberta, a correção, a validação e a prevenção contínuas ajudam as equipes de segurança a acompanhar esse ritmo.

illustration hero ai
Blog

O furacão da IA chegou

A IA está acelerando tanto a criação de software quanto os ataques cibernéticos. As lideranças devem proteger agentes e código desde o início, aplicar controles em tempo de execução e validar as defesas de forma independente.

feature insights context
Blog

A prevenção é essencialmente um problema resolvido?

A prevenção em código gerado por agentes está resolvida do ponto de vista arquitetural — mas escolher controles que protejam a segurança sem desacelerar o desenvolvimento continua sendo um desafio.