Previna-se contra a Injeção SQL


 Hoje eu vou mostrar como evitar a injeção SQL.

A SQL - Structured Query Language - é largamente usada para interagir com banco de dados relacionais. Se você considerar que 90% das aplicações utilizam banco de dados com suporte a SQL vai concluir que o uso da SQL é quase uma unanimidade por ser prática , fácil e portátil.

Em se falando de aplicações Web temos uma grande utilização de banco de dados para armazenar as mais diversas informações :  endereços e documentos pessoais , contas e valores financeiros , números de cartões de crédito , dados empresariais , etc.

Ao colocar sua aplicação na Web você a esta expondo a um acesso mais amplo e indiscriminado. Afinal qualquer um que tenha acesso a url do site terá acesso a sua aplicação e aos dados que ela disponibiliza. Pensando na segurança de suas informações as  empresas investem pesado em firewalls , certificação digital e outros recursos , com o objetivo de se proteger de invasores.

Para que o controlar o acesso as informações normalmente restringe-se o acesso aos usuários cadastrados usando um nome e senha para identificação ; estes dados são colhidos através de um formulário de login e são então verificados com as informações armazenadas em um banco de dados dos usuários cadastrados; se estiverem corretas  o acesso é permitido caso contrário o acesso é negado.

É assim que funciona o home banking na internet e uma infinidade de outras aplicações web na qual o acesso é restrito.

Você pode ter o aparato mais moderno em termos de tecnologia de segurança protegendo o seu site de um ataque hacker e nem se dar conta de que a vulnerabilidade da sua aplicação esta ali naquele formulário de login. Ele pode ser a porta de entrada para ataques maliciosos através da injeção de SQL.

A injeção SQL ocorre quando um invasor consegue inserir comandos SQL na instrução SQL que você usa no seu script de modo a burlar a restrição e ter acesso ou danificar as informações armazenadas no seu banco de dados.

Neste artigo eu vou mostrar como a injeção de SQL ocorre e falar sobre algumas das medidas que você pode tomar para evitá-la. Embora as informações sejam focadas em páginas ASP e banco de dados SQL Server /Access elas se aplicam a qualquer script e banco de dados que usam um dialeto SQL.

Como ocorre a injeção SQL

Se você acha que não deve levar a sério a injeção SQL veja esta notícia da INFO:

500 mil sites sofrem injeção de SQL

Sexta-feira, 25 de abril de 2008 - 12h54

500 mil sites !!!! Você gostaria que o seu estive entre um deles ??? Então continue a ler...

Abaixo temos um típico formulário de login :

form name="frmLogin" action="login.asp" method="post">
   Nome : <input type="text" name="nomeUsuario">
   Senha: <input type="text" name="senhaUsuario">
   <input type="Enviar">
</form>

Geralmente quando o usuário clicar no botão - Enviar - o script login.asp será executado para efetuar a validação dos dados informados. Abaixo temos um script típico para um arquivo de validação de informações:

<%

dim nomeUsuario, senhaUsuario, consulta
dim conn, rS

nomeUsuario = Request.Form("nomeUsuario")
senhaUsuario = Request.Form("senhaUsuario")

set conn = server.createObject("ADODB.Connection")
set rs = server.createObject("ADODB.Recordset")

consulta = "select count(*) from usuarios where nomeUsuario='" & nomeUsuario & "' and senhaUsuario='" & senhaUsuario & "'"

conn.Open "Provider=SQLOLEDB; Data Source=(local);Initial Catalog=myDB; User Id=sa; senhaUsuario="

rs.activeConnection = conn
rs.open consulta

if not rs.eof then
    response.write "Acesso Concedido"
else
    response.write "Acesso Negado"
end if

%>

Vamos analisar o que ocorre quando um usuário tenta se autenticar. Vamos supor que ele é usuário cadastrado com nome Macoratti e senha 123456 (só suposição). Ao informar o nome e a senha e clicar no botão Enviar o script do arquivo login.asp será executado. Vamos ver como ficou a instrução SQL montada neste caso :

select count(*) from usuarios where nomeUsuario='macoratti' and senhaUsuario='123456'

Neste caso o usuário macoratti, senha 123456 será autenticado e terá acesso ao sistema. Tubo bem ?

Não ,  se você usa este tipo de instrução SQL nada esta bem  pois o texto final da consulta SQL depende inteiramente do conteúdo das variáveis , e ,  se o conteúdo destas variáveis não for validado e tratado o texto final concatenado poderá ser um SQL adulterado através de uma injeção SQL.

Quer ver ? Vou começar pegando leve. Vamos supor que um hacker decidiu invadir sua página. Uma das primeiras coisas que ele pode fazer é tentar uma injeção SQL , e,  vai começar verificando se você esta tratando o apóstrofe (aspa simples: '). Se você não sabe a presença de um caractere de apóstrofe (') no conteúdo de uma variável concatenada no SQL é usada para delimitar strings de texto. Então suponha que ele digite os seguintes dados nos campos nome e senha:

nome = tes'te
senha =

Se você não tratar o apóstrofe vai ocorrer um erro de SQL (incorrect Sintax) ,e , vendo isto o hacker vai ficar mais animado...

Agora vou pegar pesado. Suponha então que a seguir ele digite os seguintes dados

nome = ' ; drop table users--
senha =

Este comando irá excluir a tabela users (se ela existir). E se você pensa que é muito difícil o hacker adivinhar o nome da sua tabela vou mostrar mais abaixo que ele pode fazer isto de uma maneira simples. Isto é possível pois o caractere (;) indica o fim de uma consulta e o começo de outra em T-SQL , e , o caractere (--) no final da linha faz com que o scrpt ASP  seja executada sem erro.

Continuando o ataque , ele pode também informar o seguinte :

nome = admin
senha = ' or 1=1--

veja como vai ficar a consulta SQL montada:

select count(*) from usuarios where nomeUsuario='admin' and senhaUsuario='' or 1=1--'

Aqui a consulta irá verificar se o nome do usuário é admin e se senha é vazio ou 1 for igual a 1 ( o que é verdade) ; bingo, se existir um usuário admin ele entrou no seu sistema.

Ele pode também tentar o seguinte :

nome = ' or 1=1--
senha =

a consulta SQL montada será :

select count(*) from usuarios where nomeUsuario='' or 1=1 --' and senhaUsuario=''

e bingo , ele burlou o seu sistema.

O hacker pode também tentar o seguinte :

nome = ' OR "='
senha = ' OR "='

e a consulta SQL montada será :

select count(*) from usuarios where nomeUsuario='' OR "=" AND senhaUsuario='' OR "="

a consulta agora esta fazendo a comparação : OR "="  que é sempre verdadeira. Bingo ele entrou no seu site.

Para saber o nome das tabelas e campos o hacker pode fazer o seguinte :

nome = '  having 1=1--
senha =

isto pode causar o seguinte erro:

Microsoft OLE DB Provider for ODBC Drivers error '80040e14'

[Microsoft] [ODBC SQL Server Driver] [SQL Server] Column 'usuarios.codigo' is invalid in the select list because it is not contained in an aggregate function and there is no GROUP BY clause.

/login.asp , line 28

e bingo , o hacker agora sabe que o nome da tabela é usuarios e o nome do campo relacionado no formulário como nome é codigo.

E então , o que você acha que ele vai fazer ?  Fazer a mesma coisa para o campo senha e então ele vai saber o nome da tabela e dos campos relacionados ao formulário. Imagine o estrago que ele não será capaz de fazer agora...

Pensa que ele pode ficar somente nisto. Já conhecendo o nome da tabela e das colunas se o hacker quiser saber o tipo de dados do campo ele pode fazer o seguinte :

nome = '  UNION SELECT SUM(nomeUsuario) FROM usuarios--
senha =

como o SQL Server vai tentar aplicar a cláusula SUM antes de determinar se o número dos campos nas duas colunas é igual. Ao tentar fazer um SUM em um campo texto o sistema pode emitir a seguinte mensagem de erro:

Microsoft OLE DB Provider for ODBC Drivers error '80040e7'

[Microsoft] [ODBC SQL Server Driver] [SQL Server] The Sum or average aggregate operation cannot take a varchar data type as na argument.

/login.asp , line 109

e bingo de novo , ele agora sabe o tipo de dado do campo nomeUsuario.

Agora sabe o que ele vai fazer ? Vai inserir um usuário com nome senha para se logar ,  assim :

nome = '  ; INSERT INTO usuarios VALUES('hacker','111111')--
senha =

e bingo , ele vai se logar como hacker e senha 111111.

Acho que com estes exemplos já deu para você perceber que você tem que cuidar com muito mais cuidado das suas instruções SQL .Tenha certeza de uma coisa : as possibilidades do hacker são muitas.

Como evitar uma ataque de injeção SQL

A seguir algumas orientações de como você pode evitar um ataque de injeção SQL :

1- Estabeleça uma política de segurança rígida e criteriosa limitando o acesso dos seus usuários. Isto quer dizer que você deve dar somente os poderes necessários aos seus usuários. Não de acesso de escrita a tabelas e dê somente acesso as tabelas que o usuário vai precisar.

2- Faça a validação da entrada de dados no formulário e não permita os caracteres inválidos como : (') , (--) e (;)  nem de palavras maliciosas como insert , drop , delete, xp_ . Abaixo algumas funções que você pode usar:

- Substituindo o apóstrofe(') pelo duplo apóstrofe ('')

<%

Function ExpurgaApostrofe(texto)
    ExpurgaApostrofe = replace( texto , "'" , "''")
End function

%>

- Substituindo os caracteres e palavras maliciosas por vazio("").

<%
Function LimpaLixo( input )

    dim lixo
    dim textoOK

    lixo = array ( "select" , "drop" ,  ";" , "--" , "insert" , "delete" ,  "xp_")

    textoOK = input

     for i = 0 to uBound(lixo)
          textoOK = replace( textoOK ,  lixo(i) , "")
     next

     LimpaLixo = textoOK

end Function
%>

- Rejeitando os dados maliciosos:

<%
Function ValidaDados( input )

      lixo = array ( "select" , "insert" , "update" , "delete" , "drop" , "--" , "'")

      ValidaDados = true

      for i = lBound (lixo) to ubound(llixo)
            if ( instr(1 ,  input , lixo(i) , vbtextcompare ) <> 0 ) then
                  ValidaDados = False
                  exit function}
            end if
      next
end function
%>

Sempre que puder, rejeite entrada que contenha os caracteres a seguir.

Caractere de entrada Significado em Transact-SQL
 ; Delimitador de consulta.
 ' Delimitador de cadeia de dados de caractere.
 -- Delimitador de comentário.
 /* ... */ Delimitadores de comentário. Texto entre / * e * / não é avaliado pelo servidor.
 xp_ Usado no início do nome de procedimentos armazenados estendidos de catálogo, como xp_cmdshell.

3- Limite a entrada de texto para o usuário no formulário de entrada de dados. Se o campo nome deve ter somente 10 caracteres restrinja a isto a entrada de dados no formulário. O mesmo vale para a senha;

4- Faça o tratamento adequado de erros não permitindo que mensagens de erros exponham informações sobre a estrutura dos seus dados;

5- Faça um log para auditoria dos erros ocorridos e das operações mais importantes da aplicação;

6- Sempre valide entrada de usuário testando tipo, comprimento, formato e intervalo;

7- Nunca construa instruções  SQL ou Transact-SQL diretamente da entrada do usuário;

8- Use procedimentos armazenados (stored Procedures) para validar entrada de usuário;

9- Nunca concatene entrada de usuário que não seja validada. A concatenação de cadeia de caracteres é o ponto principal de entrada de injeção de script;

10 - Teste o conteúdo de variáveis de cadeia de caracteres e só aceite valores esperados. Rejeite entradas que contenham dados binários, seqüências de escape e caracteres de comentário. Isso pode ajudar a impedir injeção de script e proteger contra explorações de excesso de buffer;

A Microsoft lançou um kit de ferramentas que pretende auxiliar desenvolvedores e administradores de websites a bloquear e erradicar a recente onda de ataques de SQL Injection (Injeção SQL).

As ferramentas, que são voltadas para desenvolvedores web e administradores de TI, buscam auxiliar a identificação de pontos frágeis em scripts ASP que poderiam ser explorados em um ataque de injeção SQL.

As ferramentas são:

Scrawlr  -   Examina os arquivos do site e simultaneamente analisa os parâmetros usados em cada página buscando por vulnerabilidades do injeção SQL.

Microsoft Source Code Analyzer for SQL Injection -  Chamada de MSCASI, esta é uma ferramenta de análise estática para análise dos códigos em ASP. Para executá-la é necessário ter acesso ao código fonte da página.

URLScan 3.1 -  Esta ferramenta restringe os tipos de solicitações HTTP que o IIS (Internet Information Services) irá processar. Ao bloquear alguns tipos específicos de solicitações, a ferramenta pode prevenir certos ataques.

Segurança é coisa séria e vale a pena tomar todas as precauções ao nosso alcance para preservar nossos dados e nossos empregos.....

Até breve...

Referências:


José Carlos Macoratti