SQL Server – Como evitar SQL Injection? Pare de utilizar Query Dinâmica como EXEC(@Query) | iMasters
SQL Server – Como evitar SQL Injection? Pare de utilizar Query Dinâmica como EXEC(@Query)
Tem 19 artigos publicados com 36100 visualizações desde 2019
Neste artigo quero compartilhar com vocês os riscos de utilizarmos Query Dinâmica no SQL Server através do comando EXECUTE e demonstrar formas seguras de se utilizar query dinâmica e que não sejam suscetíveis a ataques de SQL Injection.
É muito importante observar que a query dinâmica não é específica apenas para consultas montadas a partir do SQL Server.
Um sistema pode fazer a mesma coisa criando uma variável string no código-fonte C#. Por exemplo, montar a string e depois envia-la para o banco de dados.
Isso também é uma query dinâmica, mas montada dentro da aplicação e para o banco de dados será apenas uma query ad-hoc que a aplicação está enviando.
Como vocês podem visualizar, as possibilidades são infinitas utilizando Query Dinâmica. Desde fazer joins e definir as colunas retornadas por um SELECT, até controlar filtros no WHERE. Um exemplo bem legal do ponto de vista de performance seria esse aqui (e que acontece demais):
Se formos analisar a query final gerada dinamicamente e a query original, cheia de OR, vemos que a query dinâmica que é executada é bem mais simples que a consulta original – isso se reflete no plano de execução e no custo de execução também:
Depois desse breve resumo sobre query dinâmica, agora vou apresentar um breve resumo sobre SQL Injection pra vocês.
Essa técnica cracker consiste em explorar as consultas dinâmicas realizadas no banco de dados para executar comandos maliciosos no banco de dados, que podem mostrar dados confidenciais, sigilosos e sensíveis e até mesmo apagar dados e arquivos no servidor de banco de dados.
Se você está pensando que essa técnica deve ser bem complexa e elaborada, está muito enganado. Ela é até bem simples de ser utilizada e um invasor pode utilizá-la em campos de texto de uma aplicação, como uma tela de Login, por exemplo, que envia uma query dinâmica para o banco a fim de verificar se o usuário e senha informados estão corretos.
E acredite em mim, existem muitas strings como essa para realizar ataques de SQL Injection.
Neste mesmo exemplo acima, em uma simples tela de logon, observem que uma string simples como "'; TRUNCATE TABLE dbo.Usuarios;–" pode causar grandes transtornos na vida de um DBA simplesmente apagando todos os dados da tabela de Usuários do sistema.
É claro que o invasor ainda precisa descobrir o nome das tabelas do sistema e, no cenário acima, essa tarefa acaba sendo complicada, pois a query não retorna muitos dados – apenas uma validação.
Mas se isso for em uma tela de consulta, por exemplo, onde os dados retornados são mostrados em uma tabela na tela, aí temos um cenário ideal para realizar um ataque ao utilizarmos uma string como "'; SELECT name, name, name, name FROM sys.tables; –" num campo de busca, por exemplo, que sabemos que está mostrando quatro colunas na tela.
E aí o invasor agora tem o nome das tabelas que existem no database – o que deixou a "brincadeira" bem mais interessante agora, pois ele pode apagar dados de qualquer uma dessas tabelas ou visualizar esses dados pelo próprio sistema. E se ele pudesse enviar os dados da tabela por e-mail?
Ou seja, a ideia aqui é mostrar o quão perigoso são ataques de SQL Injection. Isso porque eu nem vou demonstrar como utilizar o xp_cmdshell para executar comandos a nível de sistema operacional no servidor.
Espero que você tenha ficado bem chocado com o impacto arrasador que um SQL Injection pode causar no seu ambiente.
O primeiro passo para conter esse tipo de ataque é limitar primeiro o alcance do ataque e depois tentamos evitar que ele aconteça.
Sim, é isso mesmo. Parece estranho, mas é que você conseguir limitar até onde um ataque desses pode chegar costuma ser um processo muito mais rápido de se implementar no ambiente, e por isso eu prefiro começar por esse passo.
Geralmente em cenários de grandes empresas, existem vários sistemas diferentes acessando a mesma instância, com dezenas de Stored Procedures com query dinâmica, fora as várias consultas ad-hoc que são fruto de query dinâmicas montadas na aplicação e enviadas para o banco.
E não venha me falar que você sempre libera permissão de db_owner não, hein! Vamos parar com isso hoje. Uma permissão de db_owner carrega implicitamente outras permissões não necessárias como IMPERSONATE, que é tão perigosa para auditorias, e também permissão de visualizar e listar os databases, mesmo que ele não tenha a permissão de VIEW ANY DATABASE.
Na grande maioria dos casos, uma aplicação pode precisar de, no máximo, permissão geral de EXECUTE no database e estar nos database roles db_datareader, db_datawriter e talvez, db_ddladmin.
Mesmo se não estiver, ele terá permissão para utilizar IMPERSONATE – o que já é bem perigoso.
Ou seja, imaginem o cenário em que você tem um sistema Web bem simples para atender uma necessidade especifica do setor comercial da empresa.
Esse sistema é só um cadastro de fornecedor, que não é nem tão crítico para a empresa. E esse sistema conecta no banco com o usuário sa.
Um invasor encontra uma brecha utilizando esse usuário sysadmin e aplica um SQL Injection na instância, que contém todos os sistemas da empresa. Dados de toda a empresa capturados e depois criptografados com sucesso! E tudo isso a partir de um sistema que nem era o Core da empresa.
Ou seja, o primeiro passo para evitar esse tipo de ataque é limitar as permissões das aplicações e em quais databases essa aplicação pode ler/escrever dados. Se um sistema não acessa/altera nenhuma tabela de outro banco, por que ele tem essa permissão?
Agora vamos mudar o nosso foco para evitar os ataques, mas precisamos identificar quais as possíveis portas de entrada para ataques de SQL Injection – ou seja, onde estamos utilizando Query Dinâmica no nosso ambiente.
A nível de aplicação e sistema, esse trabalho deve ser realizado pela equipe de Sistemas/Desenvolvimento, varrendo todo o código-fonte em busca de locais que utilizem query dinâmica e aplicando o tratamento disso na aplicação, até porque quando a consulta é montada na aplicação, o banco não tem como diferenciar o que é query dinâmica ou não para tentar tratar isso no banco.
A nível do banco de dados, quando existem Stored Procedures que utilizam dessa técnica de query dinâmica, podemos utilizar uma query T-SQL para identificar todas essas SPs e avaliar quais podem ser utilizadas para ataques de SQL Injection.
Agora é só identificar, validar e analisar as consultas que aparecem nessa consulta e verificar se elas são suscetíveis a ataques de SQL Injection e, sempre que possível, trocar o comando EXECUTE por sp_executesql. No próximo tópico vou mostrar o motivo.
A nível de aplicação e sistema, esse trabalho deve ser realizado pela equipe de Sistemas/Desenvolvimento, varrendo todo o código-fonte em busca de locais que utilizam query dinâmica e aplicando o tratamento disso na aplicação, como eu já havia comentado no tópico anterior.
Existem várias formas de se evitar isso na aplicação, como consultas parametrizadas, que vão barrar qualquer tipo de SQL Injection que um invasor tente atacar.
A nível de banco de dados, você deve utilizar a query que compartilhei no tópico anterior para identificar as consultas que utilizam query dinâmica.
O primeiro passo é analisar se realmente essa consulta precisa ser dinâmica. Acreditem, já vi muitos casos em que se utilizava query dinâmica sem necessidade alguma, como vou demonstrar abaixo:
Bem mais seguro agora e ficamos livres do SQL Injection nesse exemplo.
E assim temos o melhor dos mundos: Query flexível e customizável, rápida e segura.
Espero que tenham gostado deste artigo e que vocês comecem a levar a segurança do seu ambiente mais a sério.
Se você está preocupado com a segurança do seu ambiente e quer a opinião de um especialista no assunto, solicite agora mesmo o check-up gratuito do seu banco de dados + análise de segurança.