Aperfeiçoando a segurança de suas aplicações WEB

 

Fala ai galera, desculpem pela demora em publicar um novo artigo, mas final de ano é sempre complicado festas, família, amigos e outras dezenas de coisas que acabam por consumir nosso tempo de forma desproporcional, além do meu TCC que tem exigido um bocado de mim, esse conjunto de coisas acabou me impedindo de publicar esse artigo antes.

Eu gostaria de desejar a todos os leitores do site um Feliz e Prospero Ano de 2007 que seja repleto de saúde e sucesso em nossas vidas.

 

Agora vamos ao que interessa, nesse artigo vou tratar de uma preocupação constante dos desenvolvedores, a segurança em manter a confiabilidade e integridade dos dados de seus sistemas WEB que freqüentemente são atacados por softwares robôs. Esses softwares aproveitam a fragilidade na validação dos dados e do usuário que os forneceu. Uma das muitas técnicas usada atualmente é denominada CAPTCHA (Completely Automated Public Turing test to Tell Computers and Humans Apart) método desenvolvido pela Universidade Carnegie Mellon que consiste em gerar dinamicamente uma imagem com texto randômico distorcido cuja intenção é garantir que o usuário é um ser humano já que será preciso ler o texto contido na imagem para reproduzi-lo.

 

Para deixar esse artigo mais interessante eu vou demonstrar como aplicar a técnica do CAPTCHA em um controle ao invés de simplesmente deixá-lo em uma página o que dificulta ser reutilizada em mais de um formulário na sua aplicação WEB.

Para gerar dados randômicos em nosso projeto vamos usar algumas classes que o Framework disponibiliza:

 

System.Random, que gera dados aleatórios a partir de uma seqüência finita de números baseada em algoritmos matemáticos.

 

System.Security.Cryptography.RandomNumberGenerator, que gera dados aleatórios baseado em um array de bytes.

Vamos utilizar as duas classes comentadas no decorrer de nossa solução, a Listagem 01 ilustra a criação dos métodos responsáveis por gerar dados randômicos utilizados para criar o background distorcido e a cor da fonte da imagem.

 

É importante dizer que vou colocar trechos pequenos de código para ilustrar já que a solução completa está disponível para download neste artigo.

 

            /// <summary>

            /// Método que gera um numero randômico dentro de um range

            /// </summary>

            private int Gerarnumero(int minimo, int maximo)

            {

                  byte[] randomnumber = new byte[4];

                 

                  RandomNumberGenerator.Create().GetBytes(randomnumber);

                 

                  int rand = BitConverter.ToInt32(randomnumber, 0);

                  int retorno = (rand % ((maximo - minimo) +1));

                 

                  return ((retorno < 0)? (retorno *= -1) : retorno) ;

            }

            /// <summary>

            /// Método que gera um numero randômico

            /// </summary>

            private int Gerarnumero(int maximo)

            {

                  byte[] randomnumber = new byte[4];

                 

                  RandomNumberGenerator.Create().GetBytes(randomnumber);

                 

                  int rand = BitConverter.ToInt32(randomnumber, 0);

                  int retorno = (rand % maximo);

                 

                  return ((retorno < 0)? (retorno *= -1) : retorno) ;

}
Listagem 01. Código que gera números randômicos.

 

A Listagem 02 ilustra a implementação do método que gera a imagem a partir das técnicas de CAPTCHA texto e background randômico.

 

            /// <summary>

            /// Método que gera imagem captcha

            /// </summary>

            private Bitmap GerarImagem()

            {                

                  RectangleF retangulo;

                  Bitmap objBmp = new Bitmap(this._intLargura, this._intAltura, PixelFormat.Format32bppArgb);

                  Graphics objGrp = Graphics.FromImage(objBmp);

 

                  objGrp.SmoothingMode = SmoothingMode.HighQuality;

           

                  retangulo = new RectangleF(0,0, this._intLargura, this._intAltura);

 

                  this._enstyleBackground = (HatchStyle) Gerarnumero(55);

 

                  HatchBrush objBrush = new HatchBrush(this._enstyleBackground, this.RandonColor());

 

                  objGrp.FillRectangle(objBrush, retangulo);

 

                  if(this._strFontFamily == string.Empty || this._strFontFamily == null)

                        this._strFontFamily = this.RandonFontFamily();

 

                  Single fontSize = Convert.ToSingle(this._intAltura * 0.4);

 

                  Font objFont = new Font(this._strFontFamily, fontSize, FontStyle.Bold);

 

                  StringFormat objStrFormat = new StringFormat();

                  objStrFormat.Alignment = StringAlignment.Center;

                  objStrFormat.LineAlignment = StringAlignment.Center;      

 

                  objGrp.DrawString(this.Text, objFont, new SolidBrush(this.RandonColor()), retangulo, objStrFormat);         

 

                        return objBmp;

            }

Listagem 02. Código que gera imagem com texto randômico.
Agora que já temos a imagem vamos criar uma classe que implemente a interface IHttpHandler para interceptar requisições http através do método ProcessRequest, como ilustra a Listagem 03.

 

      public class GerarCaptchaHandler : IHttpHandler

      {

            /// <summary>

            /// Método que intercepta requisição HTTP

            /// </summary>

            public void ProcessRequest(HttpContext context)

            {

                  string texto = context.Request.QueryString["strcaptcha"];

                  ImagemCaptcha img = null;

 

                  if(texto != string.Empty && texto != null)

                        img = (ImagemCaptcha) context.Cache[texto];               

                 

                  if(img == null)

                        img = new ImagemCaptcha();

 

                  img.Imagem.Save(context.Response.OutputStream, System.Drawing.Imaging.ImageFormat.Jpeg);

 

                  context.Response.ContentType = "image/jpeg";

                  context.Response.End();

            }

        }

Listagem 03. Código da classe GerarCaptchaHandler.

Esse método sem que seja feita uma modificação no web.config não tem utilidade alguma, por isso é preciso inserir o código como da Listagem 04 no seu arquivo de configuração.

 
<httpHandlers>

      <add verb="GET" path="GerarCaptcha.aspx" type="CustomControl.GerarCaptchaHandler, CustomControl" />

  </httpHandlers>
Listagem 04. Código para interceptar requisições

Verb

Determina que tipos de requisições seram interceptadas (GET, POST, *).

Path

Indica o nome da página que será requisitada.

Type

Indica o namespace da classe responsável por interceptar a requisição.

Tabela 01. Com a descrição dos itens da tag de configuração httpHandlers.

 

Bom, agora que já temos a classe que trata requisições http para interceptar as chamadas do controle, temos também uma classe para criar a imagem e o texto randômico vamos à construção do controle.

É importante salientar que essa nova classe deve implementar a classe control para que tenhamos as mesmas propriedades do controle já existente no ASP.NET, além de implementar a interface INamingContainer que é responsável por garantir o ID único para o nosso controle e por fim a interface IPostBackDataHandler pra que nosso controle implemente o evento LoadPostData.

 

Como a cada post dado no formulário vai atualizar a imagem antes da renderização completa da página temos que armazenar o conteúdo do controle no momento do clique e para isso vamos utilizar o cachê do ASP.NET que será identificado através de seu ID um GUID que é um id gerado randomicamente e é formado por letras e números pra impedir que se repita.

 

Armazenando conteúdo no Cachê:

this.strGuide = Guid.NewGuid().ToString();

HttpContext.Current.Cache.Add(this.strGuide,this._objCaptcha,null,DateTime.Now.AddMinutes(HttpContext.Current.Session.Timeout),TimeSpan.Zero,
    System.Web.Caching.CacheItemPriority.NotRemovable, null);

Listagem 05. Código para armazenar controle no cachê.

 

Finalmente vamos criar uma classe para customizar o evento do nosso controle, pra isso é preciso criar uma nova classe que implemente o namespace EventArgs pra que seja gerado um evento de validação do controle.

 

A Listagem 06 mostra o código desta classe:

public class MyClickEventArgs : EventArgs

       {

             private bool _bitValido;

 

             public bool bitValido{

                    get{ return this._bitValido ; }

                    set{ this._bitValido = value; }

             }

       }
Listagem 06. Código da classe MyClickEventArgs que customiza o evento do controle.

 

Para consumir este controle em suas páginas como os demais já existentes no ASP.NET basta adicioná-lo na Toolbox, seguindo os passos abaixo:

 

  1. Clique com o botão direito do mouse sobre a toolbox
  2. Selecione a opção “Add/Remove items...”
  3. Na próxima tela selecione a .dll gerada pelo projeto CAPTCHA

Pronto agora é só clicar e arrastar e configurar o controle para o seu formulário.


A Figura 01 ilustra o uso do controle desenvolvido neste artigo.


captchanetmagfig01.JPG

Figura 01. Demonstração do controle CAPTCHA.

 

            /// <summary>

            /// Required method for Designer support - do not modify

            /// the contents of this method with the code editor.

            /// </summary>

            private void InitializeComponent()

            {   

                  this.DCtrlCaptcha1.Validate += new CustomControl.ClickEventHandler(this.DCtrlCaptcha1_Validate);

                  this.Load += new System.EventHandler(this.Page_Load);

 

            }

           

            private void DCtrlCaptcha1_Validate(bool valido)

            {

                  if(valido)

                        this.Label1.Text = "Válido.";

                  else

                        this.Label1.Text = "Inválido.";

            }

Listagem 07. Código do evento de validação do controle.

 

Espero que o artigo seja útil e que todos tenham gostado do assunto e da forma que abordei o tema, por ser uma solução um pouco extensa e com alguns poucos pontos onde a complexidade é alta não pude dar detalhes maiores, porém podem tirar dúvidas através dos comentários.