SQL Injection é uma técnica de manipulação de código SQL, que torna possível explorar eventuais brechas de codificação, para que sejam extraídas (ou destruídas) quaisquer que sejam as informações contidas em um banco de dados.
Mesmo pensando em se tratar de um assunto que hoje em dia é de conhecimento público, existe uma série de questões de segurança e boas práticas que deve-se levar em conta, que vão além da implementação correta do código que faz acesso ao banco de dados.
Ainda assim, é possível encontrar diversos sites ou aplicativos que estão vulneráveis devido a essa implementação deficitária do código, seja por desconhecimento na hora da implementação, ou a falta de cuidado aos demais detalhes que envolvem “codar” uma aplicação.
Motivos que permitem o SQL Injection explorar brechas
Existem dois motivos principais que tornam possível o SQL Injection explorar alguma brecha:
- A aplicação confia nos dados arbitrários fornecidos pelo usuário.
- O usuário que executa a consulta, tem níveis de permissão mais elevados do que o necessário.
Um dos erros mais comuns acontece justamente na tela de login, na qual é solicitado que o usuário informe as credenciais que são passadas como parâmetros para a aplicação, que constrói um comando SQL e executa diretamente no banco, sem antes ter sido feito qualquer tipo de validação do dado, da seguinte forma:
select * from dbo.Usuarios as U where U.usuario = ‘ + txtUsuario.Text + ‘ and U.senha = ‘ + txtSenha.Text + ‘
Alguém com algum conhecimento básico de SQL consegue montar um código, que faz com que essa consulta seja válida, independente do que seja digitado nos campos de texto do formulário de login. Por exemplo, ao informar o parâmetro “‘ or 1 = 1” no campo senha, a seguinte consulta é construída e executada no banco:
select * from dbo.Usuarios as U where U.usuario = ‘‘ and U.senha = ‘’ or 1 = 1
Para quem tem o conhecimento, não existem limites?
É muito mais comum do que se imagina a aplicação efetuar a tratativa de erros que acontecem ao acessar o banco de dados, retornando exatamente a mensagem do erro para o usuário final. Isso é outra falha de codificação que possibilita conseguir qualquer informação contida no banco de dados. Por exemplo, ao informar no formulário de login o parâmetro “‘ having 1 = 1”, teremos o seguinte resultado na mensagem de erro do banco de dados:
select * from dbo.Usuarios as U where U.usuario = ‘‘ and U.senha = ‘’ having 1 = 1
Column ‘dbo.Usuarios.usuario’ is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
Com essa informação em mãos, o invasor já descobriu que existe no banco de dados uma tabela chamada Usuarios, e que tem um campo usuario nessa tabela. Com algum esforço, é possível descobrir qualquer outro campo na tabela, utilizando as informações que já são conhecidas informando agora, o seguinte parâmetro: “‘ group by usuario having 1 = 1’”:
select * from dbo.Usuarios where usuario = '' and senha = '' group by usuario having 1 = 1
Column ‘dbo.Usuarios.senha’ is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
Utilizando-se da técnica acima é possível saber qual o tipo de dado de cada campo da tabela, e com essa informação é possível gerar um erro de conversão de tipos que irá expor inclusive o conteúdo da tabela na mensagem de erro, utilizando-se do seguinte parâmetro “‘ union select usuario + ‘/’ + senha, 1, 1 from dbo.Usuarios”:
select * from dbo.Usuarios where usuario = '' and senha = '' union select usuario + '/' + senha, 1, 1 from dbo.Usuarios
Conversion failed when converting the nvarchar value ‘MeuUsuario/12345’ to data type int.
Nos casos mais extremos, em que o usuário utilizado pela aplicação tem permissões elevadas (dbowner por exemplo), é possível até mesmo causar um grande desastre no banco de dados para a empresa. O que aconteceria se utiliza-se o seguinte parâmetro no formulário de login: “‘; drop table Usuarios” ?
Como evitar o SQL Injection
Existe uma série de medidas que devem ser implementadas, não somente para evitar o SQL Injection, mas sim como forma de aumentar a segurança como um todo do banco de dados:
- Utilize a parametrização da forma adequada: Mesmo que seja imputado código malicioso, ele será tratado como uma string, independente do conteúdo. Com isso, irá efetuar a busca no banco, não retornando nenhum resultado caso os valores de entrada sejam inválidos. Codificar dessa forma, também tem outros benefícios para o banco de dados, como por exemplo, a tipagem correta do dado (evitando conversões implícitas), menor número de planos de execução criados no plan cache (toda vez que uma consulta que a string não seja exatamente a mesma, um novo plano de execução é criado e armazenado pelo banco de dados), reutilização dos planos de execução previamente criados (caso o plano já exista, não é necessário para o banco criar um novo, sendo assim, o Query Optimizer pode pular uma etapa, tendo um trabalho a menos na hora de processar uma consulta).
declare @usario nvarchar(100) declare @senha nvarchar(100) select U.senha from dbo.Usuarios as U where U.usuario = @usuario and U.senha = @senha
- Crie usuários com as permissões adequadas: Defina o mínimo possível de permissões para o usuário da aplicação, para que ele somente possa executar o que realmente é necessário para seu trabalho. Por exemplo: O usuário apenas lê e escreve no banco de dados:
exec sp_addrolemember N'db_datareader', N'MeuUsuario' exec sp_addrolemember N'db_datawriter', N'MeuUsuario'
Não é preciso comentar que é uma falha grave disponibilizar o SA (System Administrator) para logar no banco pela aplicação. Esse usuário pode fazer qualquer atividade no servidor do banco de dados.
- Trate corretamente as mensagens de erro do banco de dados: Nunca retorne diretamente as mensagens de erro do banco de dados para o usuário final. Capture-as e dê uma tratativa mais amigável ou genérica para o usuário final. Assim, as informações importantes não serão expostas aos usuários da aplicação.
- Habilite os logs de segurança: Isso permite auditar as tentativas de logins no banco de dados que foram rejeitadas.
- Desabilite as “extended procedures”: Caso não sejam realmente utilizadas, extended procedures como a xp_cmdshell, xp_regread, xp_servicecontrol, etc…, devem ser desabilitadas, para que não seja possível executar procedimentos no servidor, que vão muito além de apenas ler as informações contidas no banco de dados.
O objetivo deste artigo, não é ensinar você leitor, como explorar eventuais brechas de sites ou aplicações que foram erroneamente codificadas. O intuito principal é justamente chamar atenção ao fato da potencialidade que ataques usando SQL Injection podem causar no servidor do banco de dados. O uso dessas boas práticas não são apenas para evitar esse tipo de ataque. Além de tornar seu código mais seguro, aumenta a segurança do seu ambiente de banco de dados como um todo
Como a EximiaCo pode lhe ajudar
Nossa consultoria e assessoria em Arquitetura de Dados atua na análise e implantação de otimização de bancos de dados melhorando significativamente a performance de sistemas e geralmente reduzindo custos. Além disso, revisamos todas as configurações dos servidores do banco de dados, levando em conta as melhores práticas de configuração e segurança necessárias para a estabilidade, performance e continuidade do seu negócio.
Se sua empresa quer promover a autonomia de seu time nas melhores práticas de performance e segurança de banco de dados, conheça nossas capacitações in company SQL na Velocidade da Luz, onde são apresentadas técnicas avançadas para a modelagem e otimização de performance e Melhores Práticas de Configuração do SQL Server, que apresenta rotinas otimizadas para manutenção, backup e monitoramento da saúde do banco de dados.