Entendendo o SQL Injection

Segundo Duarte (2007), nas últimas décadas observou-se um aumento expressivo no desenvolvimento de tecnologias computacionais. Entretanto, toda esta evolução é acompanhada por um número crescente de sistemas sendo sondados, invadidos, infectados, comprometidos e utilizados para gerar ataques a outros sistemas. Todos estes problemas de segurança fazem parte do cotidiano de instituições, e de usuários domésticos. Desta forma, observa-se que a segurança é um aspecto muito importante, seja para o desenvolvimento desktop ou para o ambiente web.

Em um mercado onde requisitos mudam constantemente e o fator produtividade é sempre primordial, alguns aspectos acabam sendo desconsiderados, sendo a segurança um requisito quase sempre esquecido ou colocado em segundo plano. No entanto, as conseqüências geradas por esta prática quase sempre são desastrosas quando as aplicações entram em produção.

Em se tratando de aplicações web, a segurança é um fator de grande importância, já que a aplicação fica disponível em qualquer parte do mundo e passível de manipulação pelos mais diversos usuários, além de também estar passível aos mais diversos ataques.

Os ataques estão cada vez mais sofisticados e sempre se aproveitando do grande número de falhas presentes nas aplicações. Podem ser destacados alguns tipos de ataques como: url manipulation, web session hijaking e SQL injection.

Motivados pela observação do grande número de sites em produção que possuem falhas em relação a segurança, este artigo terá como foco os ataques do tipo SQL injection, visto a sua simplicidade e em contrapartida os grandes problemas que o mesmo pode trazer.

SQL Injection

Em um cenário onde existem informações armazenadas em bancos de dados e que podem ser acessados via web, tem-se a possibilidade do ataque do tipo SQL injection. SQL injection é um tipo de ataque muito simples, que baseia-se na execução de comandos SQL, sejam comandos de manipulação de dados - DML (select, insert, update, delete) ou comandos de definição de dados - DDL (create, drop, alter). Estes comandos são executados através das entradas de formulários web, ou seja, no local destinado para digitação de informações pelo usuário, onde são passados comandos SQL, que por falhas nas aplicações acabam por resultar em alterações no banco de dados ou no acesso indevido à aplicação.

A figura 1 apresenta um exemplo clássico de tal prática numa tela de autenticação:

Exemplo de SQL Injection

Figura 1 - Exemplo SQL injection

O problema ocorre devido a autenticação do usuário ser validada internamente pela aplicação com a seguinte instrução, que verifica se existe um usuário com o respectivo login e senha:

  SELECT * FROM tabela_usuarios WHERE login = 'campo_login' AND senha = 'campo_senha'

Normalmente, seriam digitados o login e a senha, que resultaria em uma consulta ao banco de dados para verificar se os mesmos conferem mas na figura acima foi passado parte de um comando SQL no campo reservado para senha, que internamente resultará na seguinte instrução:

  SELECT * FROM tabela_usuarios WHERE login = '123' AND senha = ' ' or '1' = '1'

Observe que o comando passado no campo da senha fez com que independente do login e senha informados, a condição seja sempre verdadeira, permitindo assim o acesso do usuário à aplicação sem o mesmo possuir a devida permissão.

Existem diversas possibilidades de comandos que podem ser executados indevidamente através da passagem de parâmetros, onde alguns exemplos podem ser observados na tabela 1:

SQL esperado Parâmetros informados SQL resultante Comantário
Campo_login Campo_senha

SELECT *

FROM tabela_usuarios WHERE login = 'campo_login' AND senha = 'campo_senha'

marcos’;--   SELECT * FROM tabela_usuarios WHERE login = 'marcos';--' AND senha = 'campo_senha' Se o usuário já souber o login (no caso, login do usuário marcos) então consegue logar sem senha, já que os caracteres “--“ são comentários no SQL
' OR 1=1 --   SELECT * FROM tabela_usuarios WHERE login = '' OR 1=1--' AND senha = 'campo_senha' Neste caso, não é necessário saber nem o login nem a senha, pois a condição “OR 1=1” sempre vai ser satisfeita e todos os comandos posteriores são comentados pelos carecteres “--”
123'; DROP TABLE produtos; --   SELECT * FROM tabela_usuarios WHERE login = '123'; DROP TABLE produtos;--  ' AND senha = 'campo_senha' Este caso é muito parecido com o primeiro mas um comando para apagar uma tabela é executado antes do comentário. Na verdade aqui o objetivo era somente apagar a tabela, por isso foi passado um login qualquer.

Apesar desse tipo de ataque poder ocasionar grandes danos ao banco de dados, ele pode ser evitado apenas com boas práticas de programação, independente da linguagem. Em seguida serão abordadas formas de prevenção na linguagem PHP e JAVA.

Prevenção do SQL Injection na linguagem PHP

De acordo com Alves (2007), para se proteger da utilização da SQL Injection, certas providências devem ser tomadas. Algumas das ações serão realizadas no servidor de banco de dados, outras devem ser garantidas pelo código fonte.

Ainda de acordo com o mesmo autor, deve-se tomar cuidado com a configuração do usuário que estabelece a conexão com o banco de dados. O ideal é que as permissões de acesso deste usuário estejam restritamente limitadas às funções que irá realizar, ou seja, para a exibição de um relatório, a conexão com o banco de dados deve ser realizada por um usuário com permissões de leitura e acesso somente às tabelas necessárias para sua operação.

Todos os valores originados da coleta de dados externos, devem ser validados e tratados a fim de impedir a execução de eventuais instruções destrutivas ou operações que não sejam as esperadas.

Um tratamento básico para a execução de querys com variáveis contendo valores informados pelo usuário (ALVES, 2007):

  $usuario = $_POST['usuario'];$senha = $_POST['senha'];$usuario_escape = addslashes($usuario);$senha_escape = addslashes($senha);$query_string = "SELECT * FROM usuarios WHERE codigo = '{$usuario_escape}' AND senha = '{$senha_escape}'";?>

Através da função addslashes() será adicionada uma barra invertida antes de cada aspa simples e aspa dupla encontrada (processo conhecido como escape). Se a diretiva de configuração do PHP magic_quotes_gpc estiver ativada, o escape é realizado automaticamente sobre os dados de cookies e dados recebidos através dos métodos GET e POST. Neste caso, não deve ser efetuado o tratamento com addslashes(). A função get_magic_quotes_gpc(), disponível nas versões do PHP a partir da 3.0.6, retorna a configuração atual da diretiva magic_quotes_gpc.

Abaixo, a query string resultante da aplicação do tratamento mencionado:

SELECT * FROM usuarios WHERE codigo = '' AND senha = '\' or 1=\'1'

Deve-se também evitar a exibição das mensagens de erro em um servidor de aplicação em produção, pois geralmente nos erros ou alertas são exibidos caminhos de diretórios do sistema de arquivos e informações à respeito do esquema do banco de dados, podendo comprometer a segurança do sistema.

Para ocultar a exibição de erros e alertas do PHP, é necessária a configuração da diretiva display_errors para off no arquivo de configurações do PHP.

Prevenção do SQL Injection na linguagem JAVA

Quando uma aplicação JAVA usa consultas “Statement”, ou seja, consultas onde a string SQL é concatenada com os parâmetros e assim executada, ela provavelmente terá problemas com SQL injection. Como ocorre no trecho de código a seguir:

String sql = "select * from tabela_usuarios where login='" + campo_login +"' and senha='" + campo_senha + "'";

Statement stmt = con.createStatement();

ResultSet rs = stmt.executeQuery(sql);

if (rs.next())

  System.out.println("Usuário Logado com sucesso.");

else

  System.out.println("Usuário ou Senha não conferem.");

De acordo com a OWASP (2008), uma forma simples do SQL injection ser evitado, quando é utilizada a linguagem JAVA, é realizar a validação das entradas, de forma que sejam evitados caracteres que não façam parte do conjunto de caracteres das entradas (inputs) e utilizar consultas “Prepared Statement”, ou seja, os parâmetros não são concatenados, a string SQL define parâmetros:

  String sql = "SELECT * FROM tabela_usuarios WHERE login = ? AND senha = ?";

PreparedStatement prepStmt = con.prepareStatement(sql);

prepStmt.setString(1,login);

prepStmt.setString(2,senha);

ResultSet rs = prepStmt.executeQuery();

Assim, variáveis passadas como prepared statement farão o escape automaticamente, da mesma forma como exposto na linguagem PHP.

Considerações Finais

Sistemas Desktop e Web ainda permanecem como alvos vulneráveis, apesar da disponibilidade de ferramentas e de procedimentos que dificultam a ocorrência de ataques. A fragilidade desses sistemas muitas vezes é potencializada pelo desconhecimento que os programadores possuem sobre a programação segura e também sobre os cenários nos quais esses ataques ocorreram e foram bem sucedidos.

Assim, este trabalho apresentou alguns procedimentos que visam auxiliar a construção de aplicações seguras sob o ponto de vista dos ataques provocados pela SQL injection.

Links Úteis

Saiba mais sobre SQL ;)

  • Guia SQL:
    Neste Guia de Referência você encontrará todo o conteúdo que precisa para aprender sobre a SQL, linguagem de consulta estruturada utilizada por programadores e DBAs para a execução de consultas e comandos nos principais SGBDs do mercado.
  • Trabalhando com a linguagem T-SQL:
    Este artigo apresenta o uso da linguagem T-SQL. Serão discutidos alguns recursos que podem ser usados no desenvolvimento e manipulação na consulta a dados usando a ferramenta SQL Server.
  • Conceitos e criação de views no SQL Server:
    Veja neste artigo como trabalhar com views no SQL Server, aprendendo como utilizar os comandos CREATE, ALTER e DROP VIEW.