| Este artigo discute | Este artigo usa as seguintes tecnologias: | ||
| ASP.NET |
Dependendo do tipo de aplicação que você está desenvolvendo, você pode esperar executar uma transação com uma outra máquina ou pode esperar que uma pessoa esteja do outro lado da transação. Quando você tem uma Aplicação Web que requeira contas de usuário legítimas, você pode ser surpreendido ao descobrir que algumas de suas contas podem pertencer a uma única pessoa, usando um script malicioso rodando em sua máquina para criar muitas contas virtuais de usuário em sua aplicação. Estas contas “virtuais” provocam o aumento do consumo de recursos de seu servidor, como largura de banda, entre outros.
O processo pelo qual tais scripts criam contas de usuário é chamado spoofing e, para a maioria dos Websites simples, pode ser realizado muito facilmente. Tudo que o spoofer precisa fazer é criar um formulário HTML que contenha os campos idênticos àqueles em seu formulário de Login e, então, enviar através de "HTTP-POST" os dados a seu servidor, onde o processo de criação de contas de usuário ocorre. O problema é ainda pior se você permitir que seus formulários de Login sejam processados através de "HTTP-GET". Após ter criado com sucesso uma conta de usuário uma vez, não há nada que possa parar o spoofer de automatizar o processo inteiro.
Com um script automatizado, os spoofers podem literalmente criar centenas de contas com um único comando. Se seu servidor não validar os dados, você arrisca ser abarrotado por uma quantidade enorme de contas "virtuais" inúteis. Se seu servidor o validar, um pequeno número de requisições pode degradar seus recursos de sistema, retardar, ou derrubar sua aplicação.
Um outro problema em potencial do spoofing ocorre porque é fácil escrever os scripts que acessam o sistema usando o mesmo usuário, através dos diferentes navegadores existentes. Embora isso possa não ser um problema para algumas aplicações, pode desperdiçar recursos como largura de banda, particularmente quando sua aplicação permite que os clientes baixem arquivos ou outros recursos. Algumas aplicações verificam se o usuário já está acessando o sistema antes de permitir que seja criada uma nova instância da aplicação em seu navegador. Múltiplos ataques nestas aplicações consomem recursos, tais como conexões a bancos de dados e memória de sistema, enquanto o servidor executa repetidamente a verificação do login.
Existem muitas formas que podemos utilizar para proteger nossas aplicações destes ataques e, nesta matéria, iremos discutir a técnica conhecida como “word-verification technology” (Tecnologia de Verificação de Frase), vantagens e desvantagens de implementação. Em nosso caso, utilizaremos o projeto CAPTCHA, desenvolvido pelo Carnegie Mellon University, que tem por finalidade diferenciar humanos de computadores.
O CAPTCHA apresenta um problema simples para os humanos resolverem, mas extremamente complexo para que os computadores resolvam em um curto período de tempo. Um método comum é a apresentação de uma imagem contendo um texto. Os usuários lêem o texto e o digitam novamente nos formulários de login.

Figura 1 - Formulário de Login
As pessoas podem, facilmente, ler o texto “kCJMB” exibido na Figura 1, mas os computadores não conseguiriam. Para obter sucesso, os programas necessitariam de softwares chamados OCR’s. No entanto, os OCR’s vêem adquirindo uma qualidade e precisão de resultados muito grande, portanto, precisamos gerar novas formas de confundi-los.
De forma a confundir os OCR’s, utilizamos fundos (background’s) coloridos, linhas, círculos e pontos gerados de forma aleatória, fazendo assim com que os OCR’s não consigam mais perceber a diferença entre as letras, fundo e os outros elementos. Somados a isso, utilizamos imagens de fundo baseadas em algoritmos fractais para dificultarmos ainda mais o reconhecimento dos caracteres.
Uma operação de login ou uma criação de usuários deve incluir uma verificação pela qual o aplicativo consiga comparar letras e/ou números de uma imagem com as letras e/ou números digitados pelo usuário. Se a comparação resultar em sucesso, pode-se supor que o operador é realmente uma pessoa e não um computador ou programa.
A simples solução de verificar estas informações no servidor não é suficiente para resolver o problema dos ataques de spoofing, pois poderá tornar-se a fonte de outro problema maior, uma vez que o atacante poderá enviar dezenas ou centenas de requisições simultaneamente ao servidor, resultando na paralisação do mesmo. Para tanto, precisamos de uma solução mais robusta e planejada que se enquadre nos seguintes princípios:
| • | Minimizar a utilização de recursos: A aplicação não deverá utilizar armazenamento de dados, seja ele em arquivos físicos ou bancos de dados. |
| • | Utilização de variáveis de sessão: Não devem ser utilizadas variáveis de sessão, pois elas serão armazenadas na memória do servidor (consumindo grande quantidade de recurso) e não são uma solução escalável para ambientes de WebFarms. |
| • | Proteção: A aplicação deverá proteger-se de múltiplos ataques de spoofing, validando a combinação entre os dados enviados pelos usuários e os caracteres CAPTCHA tanto no Cliente quanto no servidor. |
Portanto, torna-se clara a necessidade de se armazenar os caracteres CAPTCHA no cliente. Os dados deverão ser encriptados para que os atacantes não possam recuperá-los e enviá-los de forma automática. Nossa aplicação de exemplo ilustra bem este conceito, utilizando o algoritmo SHA1 para a geração de Hash.
Um Hash pode ser gerado a partir de uma string (letras e/ou números) e representam com uma pequena quantidade de caracteres seu valor original. Os algoritmos de Hash são extremamente rápidos na geração das saídas e as geram de forma que sua decodificação torna-se um processo muito lento e de difícil realização.
Nesse caso, podemos aplicar um Hash dos caracteres CAPTCHA gerados no servidor e armazená-los em Cookies no cliente. Após o preenchimento do formulário, devemos comparar os dados digitados pelo cliente (após gerarmos um Hash destes) com os caracteres CAPTCHA, já com o Hash que foi previamente adicionado no Cookie deste mesmo usuário.
Embora seja possível criar-mos uma página que retorne tanto a imagem como os caracteres gerados, existe o fato de não podermos retorná-los ao mesmo tempo, pois para podermos retornar uma imagem JPG/JPEG devemos ajustar o Content-Type da página para “image/jpeg” e para retornar um HTML devemos ajustar o Content-Type para "text/html”. Dessa forma, a maneira mais prática para a solução do problema é gerarmos duas páginas. Uma para retornar a imagem e outra para conter o texto gerado.
Quando o servidor estiver criando a página com o texto e ela incluir a Tag <img src=" RandomImage.aspx">, o browser a analisará e requisitará uma nova imagem à página geradora que irá processar uma nova string CAPTCHA e retornará a imagem contendo os caracteres gerados. Para que o browser possa reconhecer a resposta da página “RandomImage.aspx” como uma imagem, o seu Content-Type deverá ser configurado como “image/jpeg”.
Devido às restrições de armazenamento das informações, a página geradora das imagens será a responsável por guardar no Cookie do usuário a string CAPTCHA, já com o Hash SHA1.
A página principal de nosso exemplo contém o formulário de login com dois campos (Usuário e Senha), um campo extra para o usuário informar os caracteres CAPTCHA, a imagem gerada com os caracteres CAPTCHA e um botão de Login.
<form id="Form1" method="post" runat="server"> <asp:TextBox id="TextBoxUsuario" runat="server"></asp:TextBox> <asp:TextBox id="TextboxSenha" runat="server"></asp:TextBox> <asp:TextBox id="TextBoxImagemText" runat="server"></asp:TextBox> <asp:Image id="Image2" runat="server" ImageUrl="RandomImage.aspx"></asp:Image> <asp:Button id="ButtonLogin" runat="server" Text="Login"></asp:Button> </form>
Lista 1 - Login.aspx: Página inicial do nosso exemplo
Note que o controle Image (asp:Image) está indicando o caminho da imagem da página “RandomImage.aspx”, que retornará diretamente a imagem gerada.
Há duas partes críticas da lógica da aplicação para fazer isso acontecer. Primeiramente você necessita um gerador aleatório de string. A função a seguir, chamada “GerarStringRandomica”, recebe como parâmetro a quantidade de caracteres que a string deverá ter. O array “_CaracteresPermitidos” define quais os caracteres poderão ser utilizados na geração da string. Todo esse algoritmo está implementado em C# na classe Randomizer.
private String[] _CaracteresPermitidos = new string[]
{"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "X", "Z",
"W", "Y", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v",
"x", "z", "w", "y", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" };
public String GerarStringRandomica(Int32 qtdCaracteres)
{
// Cria Objetos
Random oRandomEngine;
String sRandomicString = String.Empty;
// Inicializa a Engine
oRandomEngine = new Random((((DateTime.Now.Second + this._CaracteresPermitidos.Length) * DateTime.Now.Hour * DateTime.Now.Millisecond) -
DateTime.Now.DayOfYear) + DateTime.Now.Millisecond + DateTime.Now.Second);
// Gera a String Randomica
for (Int32 countTMP = 0; countTMP < qtdCaracteres; countTMP++)
{
sRandomicString += this._CaracteresPermitidos[oRandomEngine.Next(0, this._CaracteresPermitidos.Length)];
}
// Retorna
return sRandomicString;
}
Lista 2 - Randomizer.cs: Trecho do código da nossa classe
Em seguida você precisa de um algoritmo de Hash para poder proteger os textos CAPTCHA gerados.
Para podermos melhorar a segurança de nosso Hash vamos adicionar um conjunto de caracteres extra aos textos CAPTCHA gerados, para tanto, utilizamos um MAC (Machine Authentication Check). Um MAC é um valor tecnicamente único que identificará cada computador (servidor) ou, em nosso caso, identificará cada aplicativo web. Para criarmos um GUID para o MAC podemos seguir um dos procedimentos abaixo:
| • | Criação Indireta:
| ||||||||||||
| • | Criação Direta:
|
<appSettings> <add key="MACKey" value="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" /> </appSettings>

Figura 2 - Utilitário de geração de GUID
Dessa forma, podemos contar com um “diferencial” de segurança entre as aplicações web existentes, garantindo assim que o texto CAPTCHA “Xpoi4H” que for gerado em uma aplicação A tenha o Hash diferente do mesmo texto CAPTCHA gerado na aplicação B.
Para proceder com a geração do Hash podemos utilizar o Algoritmo abaixo:
1. | Recuperamos o nosso MAC armazenado no Web.Config; |
2. | Concatenamos a nossa string (source) com o nosso MAC (sMAC) e utilizamos o Objeto System.Text.UnicodeEncoding.Unicode para recuperarmos um array (com a representação de cada letra e/ou número em byte) com os elementos da string; |
3. | Instanciamos o componente SHA1; |
4. | Calculamos o Hash da nossa (originalmente) string concatenada; |
5. | Convertemos cada uma das posições do array do Hash para sua representação em HexaDecimal. |
public String HashMAC(String source)
{
// Cria Objetos
Byte[] bHashValue;
Byte[] bMessage;
String sReturn = String.Empty;
String sMAC = String.Empty;
SHA1Managed oSHA1Managed;
// Recupera MAC (1)
sMAC = new AppSettingsReader().GetValue("MACKey", typeof(String)).ToString();
// Converte String para Array de Bytes (2)
bMessage = UnicodeEncoding.Unicode.GetBytes(String.Format("{0}{1}", source, sMAC));
// Instancia SHA1Managed (3)
oSHA1Managed = new SHA1Managed();
// Calcula o HASH do Texto (4)
bHashValue = oSHA1Managed.ComputeHash(bMessage);
// Calcula a Representação em Hexadecimal da String (5)
foreach (Byte tmpByte in bHashValue)
{
sReturn += tmpByte.ToString("X2");
}
// Retorna uma Representação Hexadecimal da String
return sReturn;
}
Lista 3 - Randozimer.cs: Trecho da classe que trabalha com a segurança do texto CAPTCHA
Para podermos gerar a imagem devemos utilizar os componentes do System.Drawing.
Devemos ainda pensar na possibilidade de podermos “enganar” os softwares de OCR existente, alterando o formato e a cor dos textos, inserindo linhas, círculos e outras formas geométricas geradas de forma aleatória na imagem final.
| • | Nossa primeira providencia é gerarmos uma nova string CAPTCHA. Passo 1. |
| • | Depois, devemos armazenar o Hash SHA1 de nossa string em um cookie no usuário, indicando que ele deverá expirar em 365 dias. Passos 2, 3 e 4. |
| • | Invocamos o método gerador da imagem, que também será responsável por “escrever” a saída “imagem” na página “OutputStream”. Passo 5. |
| • | Finalmente indicamos a limpeza do cachê. Passo 6 |
private void Page_Load(object sender, EventArgs e)
{
// Cria Objetos
String sChaveAntiSpoof = String.Empty;
HttpCookie oCookie;
Randomizer oRandomizerEngine;
// Carrega nova Chave (1)
oRandomizerEngine = new Randomizer();
sChaveAntiSpoof = oRandomizerEngine.GerarStringRandomica(_QtdCaracteresImagem);
// Armazena String no Cookie (2)
oCookie = new HttpCookie(_MACCookieName);
oCookie.Value = new Randomizer().HashMAC(sChaveAntiSpoof);
// Configura Cookie para Expiração de 1 Ano (3)
oCookie.Expires = DateTime.Now.Add(new TimeSpan(365, 0, 0, 0));
// Adiciona Cookie (4)
HttpContext.Current.Response.Cookies.Add(oCookie);
// Desenha Imagem (5)
this.DesenhaImagem(sChaveAntiSpoof, true, eTamanhosImagem.t200x50);
// Limpa Cache (6)
this.Response.Expires = -1;
}
Lista 4 - RandomImage.aspx.cs: Trecho do código responsável por gerar a imagem
Agora devemos validar os dados fornecidos pelo usuário, para podermos garantir que realmente ele será uma pessoa e não um computador.
Para tanto, invocamos o método “ValidarImagem” de nossa página “RandomImage.aspx”. Esse método nos confirmará se o texto informado pelo usuário é o mesmo que foi gerado na figura pela página “RandomImage.aspx”.
private void ButtonLogin_Click(object sender, EventArgs e)
{
if (new RandomImage().ValidarImagem(this.TextBoxImagemText.Text))
{
this.LabelResultado.Text = "(Autenticação Realizada)";
}
else
{
this.LabelResultado.Text = "(Descrição da Imagem Inválida)";
}
// Zera Textbox
this.TextBoxImagemText.Text = "";
}
A proteção contra ataques de Spoofing pode e deve se tornar uma prática muito comum e altamente necessária, uma vez que os ataques a aplicativos web tendem a crescer. E também por causa da alta valorização de um bem muito disponível e muito disputado em nosso dia a dia, a informação. Aliado a isso, não devemos jamais esquecer de prever os impactos que nossas soluções terão, pois de nada adianta protegermos nossos aplicativos de ataques de spoofing e torná-los vulneráveis a ataques DDOS.
How to Spoof-proof Your Logins – artigo de William Tay (em inglês)
http://www.devx.com/dotnet/Article/21308/1954?pf=true
* Andre L. Sampaio (andre.sampaio@forumaccess.com.br) é Engenheiro Eletrônico formado pela Universidade Mackenzie, com 6 anos de experiência na área de TI e há 1 ano no cargo de Analista de Sistemas na FórumAccess e Guilherme B. Moralez (guibacellar@uol.com.br) é Bacharel em Ciências da Computação, desenvolvedor em ASP.NET há 3 anos, Analista Programador da FórumAccess Consultoria e MCP Asp.Net.
SPOOF - Nós devemos a palavra spoof a um comediante britânico chamado Arthur Roberts (1852-1933), que inventou um jogo chamado Spoof que envolvia “artifício” e “absurdo”. O verbo spoof foi registrado pela primeira vez em 1889, também no sentido de "iludir-se." Nesses sentidos, agora são usados menos extensamente do que no sentido de "paródia” ou “imitação satírica”, registrado pela primeira vez em 1958, e no sentido de "satirizar delicadamente," registrado pela primeira vez em 1927.
CAPTCHA - Completely Automated Public Turing Test to Tell Computers and Humans Apart
OCR’s - Optical Character Recognition / Reconhecimento Ótico de Caracteres: são softwares capazes de reconhecer letras e números em imagens a fim de conseguirem gerar um texto com o conteúdo original da imagem.
SHA1 - Secure Hash Algorithm 1 (Algoritmo de Resumo Seguro): é um algoritmo utilizado para a geração de Hash’s.
HASH - É uma equação matemática que utiliza o texto de um documento eletrônico para criar um código chamado message digest (sumário de mensagem).
GUID - Globally Unique Identifier (Identificador Global Único): é um identificador alfanumérico usado para identificar uma fonte (seja ela um programa, componente ou recurso) de forma única. Seu número é composto por 32 caracteres (32 Bytes) o que resulta em até 32^36 possibilidades.
System.Text: - O namespace System.Text do .NET Framework contém os principais métodos para a manipulação de textos em suas diversas codificações.
System.Drawing - O namespace System.Drawing do .NET nos permite construir e manipular todos os tipos de representações gráficas existentes, como desenhos, gráficos, imagens com textos, linhas, formas geométricas, etc.
Box 1 - Para saber mais