vasão d
e Privacid
O programador, iniciante ou não, pensa em criar
uma área restrita para o site, necessitando de um login e senha
para os usuários. Isso é criado dentro do banco de dados
(em SQL ou MDB, sendo este último o mais comum) em uma tabela chamada
Users, com alguns campos, dentre eles: Usuário, Senha, Nome e Admin.
Esses campos informarão exatamente o que o nome diz, ou seja, o
login do usuário, a sua respectiva senha, seu nome e um campo flag
indicando se é admin do site ou não. Se for admin, geralmente
tem acesso a cliques extras, do tipo incluir, editar ou debilitar alguma
informação.
Feito isso, ele cria dentro da sua pagina um bloco onde pede o login e
senha para o usuário ter acesso a estas áreas. Geralmente,
o formulário tem apenas os dois campos: usuário e senha.
Estes dois campos são enviados para um script ASP, que validara
ou não o login informado.
Se for valido, redireciona-o para a área restrita; senão,
o internauta é levado de volta ao login ou, no máximo, é
informado de que algum erro foi cometido.
Bonito, não? Teoricamente funciona.
Dentro desse script ASP, o programador colocou algo desse
tipo:
Isso pega o usuário e senha informados no formulário:
cUser = trim(request("usuario"))
cSenha = trim(request("senha")) |
O código abaixo verifica no banco de dados se o
usuário e senha conferem (vamos supor que o banco já esteja
aberto com o nome de objConn):
SQLOpen = "select usuario, senha,
nome, admin from Users where
usuario = '"& cUser &'" and senha = '"
& cSenha &'"
objRS.OpenSQLOpen, objConn |
A seguir é verificado se encontrou um usuário
com o login e senha informados:
If not objRs.bof then
response.write "Bem vindo" &
objRS.fields("nome")&"!"
else
response.write"Login invalido."
end if |
Na pratica inocente, isso funciona. Funciona muito bem.
Se a senha não for a correta, o usuário realmente não
entra. Se um usuário foi digitado errado, também não
da acesso.
Mas, na prática hacker, isso funciona melhor ainda, pois permite
entrar como qualquer usuário no sistema. Até mesmo no usuário
de admin..
"select usuario , senha, nome, admin
from Users where
usuario = '" & cUser & "' and senha = '"
& cSenha &"'" |
Essa string é do SQL. Em VB e ASP, sabemos que
para contatenarmos uma string dentro de outra devemos usar aspas simples,
em vez de aspas normais (duplas), pois as aspas normais são para
a string mestra e as aspas simples são para a string interna.
Traduzindo a String acima, teríamos
select usuario, senha , nome, admin
from Users where usuario = 'geek' and
senha = 's3nh4' |
Desta forma, trocamos as variáveis cUser e cSenha
pelos seus respectivos conteúdos.
Repito, isso funciona muito bem quando usamos de forma inocente. Vale
lembrar que de dez sites em ASP que pedem login e senha, oito têm
essa forma de consulta, e estão sujeitos a algum tipo de invasão,
dependendo do nível de acesso que permitem aos seus usuários.
"Você falou, falou, falou... Mas e daí!? Cadê
o erro nisso?"
Se quando formos digitar um login, tivermos essa string
de programação do SQL na cabeça, podermos formar
outra facilmente, que injetaria um comando de SQL dentro do que o programador
já fez.
Ou seja, se eu digitar Mario no username, o SQL ficará:
select usuario, senha, nome, admin from
Users where
usuario = 'Mario' and senha = 's3nh4' |
Repare que as aspas simples continuam e fazem realmente
parte do comando, que mostra ao SQL que aquele campo deve ser comparado
com um dado do tipo string.
Agora, se digitarmos no usename Ma'rio (com uma aspa simples no meio),
a página dará um erro, pois o comando ficaria desse tipo:
select usuario, senha, nome, admin
from
Users where
usuario = 'Ma'rio and senha = 's3nh4' |
Analisando, percebemos que quando vamos comparar o campo
usuário, abrirmos uma aspa simples, colocamos o conteúdo.
Ma e fechamos a aspa simples. Para o SQL, a comparação terminou
aí, o que vem depois deveriam ser comandos.
Mas não era.
Era a continuação de usename, a palavra rio e mais uma aspa
simples, que deveria estar fechando a primeira (antes da palavra Ma),
mas na realidade está abrindo uma nova string no SQL e, como não
é comparado com nada, o SQL retorna erro de programação.
Então, já que o SQL aguarda ansiosamente por outra aspa
simples para fechar aquela primeira, por que nós não aproveitamos
e injetamos um comando nele? Imagine se usarmos a string 'or' I (isso
mesmo: aspa simples = + espaço + or + espaço + aspa + simples
+ I). A string ficaria assim:
select usuario, senha, nome, admin
from Users where
usuario = " or'l' and senha = 's3nh4' |
Lendo o comando, seria a mesma coisa que falar para o
SQL: retorne o usuário que seja igual a vazio OU l. Lembrando que
l em informática é a mesma coisa que True (verdadeiro).
Lendo novamente retorne o usuário que seja igual a vazio (não
existe nenhum) OU verdadeiro (opa...verdadeiro é verdadeiro, então
achei). Nisso, a tabela pega todos os usuários, pois todos são
validos. Agora falta só filtrar a senha.
Se usarmos a mesma string mágica na senha, nós seremos o
primeiro usuário da tabela, pois:
select usuario, senha, nome, admin
from Users where
usuario = 'l' and senha = " or'l' |
Retorne o usuário que seja igual a vazio (nenhum)
OU verdadeiro (todos). E que tenha a senha igual a vazio (nenhum) OU verdadeiro
(todos).
Isso traz todos os usuários da tabela, porém com o ponteiro
no primeiro usuário.
Quando fazemos uma tabela de usuários e colocamos no ar, qual o
primeiro usuário que incluímos? Nós mesmos, claro.
E com nível de administrador. E é exatamente esse que viramos
quando usamos essa falha.
Alguns outros casos, por exemplo, ocorrem quando queremos entrar com o
username de uma determinada pessoa. No username, colocamos o nome dela
corretamente, e na senha, como não sabemos, usamos essa string
que nos foi enviada sabe lá por quem.
O SQL, muito esperto, entende que é para retornar o usuário
com o nome informado e com a senha igual a vazio OU verdadeiro. Ou seja,
na verdade, ele vai ignorar a senha, e apontará para o registro
que o username é igual ao que foi informado no campo do formulário.
Outro ponto é quando não sabemos o nome do usuário,
e o site tem muitos cadastros. Então, entramos como qualquer um,
e com seus respectivos direitos. No usuário colocamos a string
mágica e, na senha, chutamos qualquer coisa, por exemplo, 123456
(num site com mais de 200 cadastros, é 99% de certeza que alguém
tenha usado essa senha). Então, o SQL apontará o registro
para o primeiro usuário que tenha essa senha no seu cadastro.
Outras senhas usadas são: 123123, 123321, 121212, 111222, o próprio
nome do site, abc, abcd, abcdef, abc123, 123abc, e coisas fáceis
desse tipo.
E no caso do login pedido ser um e-mail, essa string não funcionara,
pois talvez exista uma validação no campo do login para
atestar que foi digitado tem um formato de e-mail (digo talvez, pois existem
sites pedindo e-mail, login, mas que não validavam nada...).
Daí, usamos a string que passa por essas validações
(como o campo de e-mail é grande, por não saber qual e-mail
do usuário, é possível utilizar essa string maior.
A string anterior é pequena para caber em qualquer campo de login
e senha).
8A
String que passa pelos e-mails |
[email protected]'or'.ll' = '.ll
Dessa forma, caso verifique se existe @, esta string passará,
pois tem uma @ só. Se verificar se tem alguma coisa antes da @,
ela válida e também passa. Se verificarem de trás
para frente na string, procurando por uma TLD válida (com um ponto
na terceira ou quarta casa, de trás para frente), encontrarão
o ponto (.) na terceira casa, que significa uma TLD brasileira (.br) ou
de outros países.
E se ainda verificarem mais para trás, por domínios, encontrarão
outros 2 pontos, o que torna esse e-mail pertencente a um domínio
com subdomínio.
"Ok. Sou dono de um site em ASP, e uso essa forma de verificação.
A gora que já me ferrou e que todo mundo vai me invadir, pode me
dizer como conserto isso?"
Claro. É para isso mesmo que eu estou falando desse erro. Para
alertar os sites que estejam com esse problema.
O problema todo é que o script só verifica
se achou ou não um usuário: não faz um check-up para
atestar a veracidade do que foi encontrado. Então, bastaria adicionar
o seguinte comando dentro daquele script:
Isso pega o usuário e senha informados no formulário:
cUser = trim(request("usuario"))
cSenha = trim(request("senha")) |
Isso verifica no banco de dados se usuário e senha
conferem (vamos supor que o banco já esteja aberto com o nome de
objConn):
SQLOpen = "select usuario, senha,
nome, admin from Users
where usuario = '" & cUser & "' and
senha = '" & cSenha &"'"
objRS. Open SQLOpen, objConn |
Verifica se achou um usuário com um login e senha
informados:
if not objRS. Bof then
if objRS.fields("usuario") = cUsuario
and
objRS.fields("senha") = cSenha then
response.write "Bem vindo" &
objRS.fields("nome")&
"!"
else
response.write"Login inválido".
end if
else
response.write"Login inválido"
end if |
Dessa forma, não há furos de aspas simples
ou aspas normais, pois o IF não se confunde com isso.
Outra forma seria tratar o caractere das aspas simples dentro dos campos
de usuário e senha, não deixando ele estar contido nestes
campos. Mas isso é um pouco mais trabalhoso.
Vale ressaltar também que esse erro não afeta somente a
Internet. Sistemas feitos em Delphi e Visual Basic com esse tipo de verificação
de usuários também estão vulneráveis a esse
erro. Portanto, não deixe de verifica-los também.
|