ASP .NET - Usando validação por imagem - CAPTCHA


Geralmente você encontra este recurso em páginas de enquete ou listas de discussão onde o usuário deve votar , dar a sua opinião ou emitir um comentário e antes de enviar deve informar o código com caracteres gerados no formato de figura distorcida da página. O objetivo é ter certeza de que não é uma máquina quem esta enviando a requisição.

Estas imagens distorcidas envolvem a técnica conhecida como CAPTCHA. Um CAPTCHA apresenta caracteres tão distorcidos que quase nenhum programa seria capaz de interpretá-los e decifrá-los de maneira automática. Teoricamente, só mesmo um humano conseguiria entendê-lo e digitar a seqüência correta, comprovando que é um humano e não um programa malicioso , spam, etc.

Veja a abaixo um trecho da definição da Wikipédia - http://pt.wikipedia.org/wiki/CAPTCHA

CAPTCHA é um acrônimo de -  Completely Automated Public Turing test to tell Computers and Humans Apart -  e envolve um computador (um servidor) que pede que um usuário termine um teste. Como os computadores são incapazes de resolver o CAPTCHA, todo usuário que incorpora uma solução correta é presumidamente humano. O termo foi inventado em 2000 por Luis von Ahn, Manuel Blum , Nicholas J. Hopper e por John Langford.

Um tipo comum de CAPTCHA requer que o usuário identifique as letras de uma imagem distorcida, às vezes com a adição de uma seqüência obscurecida das letras ou dos dígitos que apareça na tela. Como o teste é administrado por um computador, em contraste ao teste padrão de Turing que é administrado por um ser humano, é descrito às vezes como um "teste reverso de Turing".

Abaixo temos exemplos de imagens CAPTCHA:

Este CAPTCHA de "smwm" distorce a mensagem para evitar a interpretação automática por computadores. Entretanto, programas foram desenvolvidos para ler este tipo de CAPTCHA[1].

CAPTCHA mais moderno. Melhora tentando criar um fundo distorcido e uns níveis elevados de entortar no texto, este CAPTCHA focaliza em fazer a segmentação e dificulta adicionando uma linha angular.

O CAPTCHA é usado para evitar ataques de robos ou bots (o termo bots vem de robots, robôs em inglês), programinhas maliciosos que automatizam operações repetitivas. Sem os captchas, esses bots seriam capazes de simular um humano digitando informações aleatórias de modo a criar centenas ou até milhares de  requisições em poucas horas.

Após esta introdução vou mostrar uma forma de implementar a validação por imagens em páginas ASP .NET.

O cenário hipotético será simular uma votação onde o usuário deve selecionar uma opção e clicar no botão votar. Apenas para ilustrar vamos propor a votação sobre um tema bem atual:  Você concorda com a liberação das pesquisas usando células tronco ?

Abra o Visual Web Developer 2008 Express Edition e crie um novo web site chamado CaptchaNet usando a linguagem VB .NET usando o template ASP .NET Web site;

Selecione a página Default.aspx e a partir do menu Table selecione Insert Table para incluir uma tabela com 7 linhas e 1 coluna na página:

Vamos criar um leiaute bem simples contendo a pergunta sobre o tema e :

Agora vamos incluir uma classe no web site; menu WebSite selecione Add New Item e em Templates selecione Class informando o nome Captcha.vb;

 

O arquivo será incluído na pasta App_Code. Agora digite o código abaixo na classe;

Imports Microsoft.VisualBasic

Imports System

Imports System.Drawing

Imports System.Drawing.Drawing2D

Imports System.Drawing.Imaging

Imports System.Drawing.Text

 

Namespace Captcha

 

''' <summary>

''' Summary description for CaptchaImage.

''' </summary>

Public Class CaptchaImage

' Public properties (all read-only).

Public ReadOnly Property Text() As String

Get

Return Me.m_text

End Get

End Property

 

Public ReadOnly Property Image() As Bitmap

Get

Return Me.m_image

End Get

End Property

Public ReadOnly Property Width() As Integer

Get

Return Me.m_width

End Get

End Property

 

Public ReadOnly Property Height() As Integer

Get

Return Me.m_height

End Get

End Property

' Internal properties.

Private m_text As String

Private m_width As Integer

Private m_height As Integer

Private familyName As String

Private m_image As Bitmap

 

' For generating random numbers.

Private random As New Random()

' ====================================================================

' Initializes a new instance of the CaptchaImage class using the

' specified text, width and height.

' ====================================================================

Public Sub New(ByVal s As String, ByVal width As Integer, ByVal height As Integer)

Me.m_text = s

Me.SetDimensions(width, height)

Me.GeraImagem()

End Sub

' ====================================================================

' Initializes a new instance of the CaptchaImage class using the

' specified text, width, height and font family.

' ====================================================================

Public Sub New(ByVal s As String, ByVal width As Integer, ByVal height As Integer, ByVal familyName As String)

Me.m_text = s

Me.SetDimensions(width, height)

Me.SetFamilyName(familyName)

Me.GeraImagem()

End Sub

Protected Overrides Sub Finalize()

Try

' ====================================================================

' This member overrides Object.Finalize.

' ====================================================================

Dispose(False)

Finally

MyBase.Finalize()

End Try

End Sub

' ====================================================================

' Releases all resources used by this object.

' ====================================================================

Public Sub Dispose()

GC.SuppressFinalize(Me)

Me.Dispose(True)

End Sub

' ====================================================================

' Custom Dispose method to clean up unmanaged resources.

' ====================================================================

Protected Overridable Sub Dispose(ByVal disposing As Boolean)

If disposing Then

Me.m_image.Dispose()

' Dispose of the bitmap.

End If

End Sub

' ====================================================================

' Sets the image width and height.

' ====================================================================

Private Sub SetDimensions(ByVal width As Integer, ByVal height As Integer)

' Check the width and height.

If width <= 0 Then

   Throw New ArgumentOutOfRangeException("width", width, "Argument out of range, must be greater than zero.")

End If
 

If height <= 0 Then

   Throw New ArgumentOutOfRangeException("height", height, "Argument out of range, must be greater than zero.")

End If

 

Me.m_width = width

Me.m_height = height

End Sub

' ====================================================================

' Sets the font used for the image text.

' ====================================================================

Private Sub SetFamilyName(ByVal familyName As String)

   ' If the named font is not installed, default to a system font.

Try

   Dim font As New Font(Me.familyName, 12.0F)

Me.familyName = familyName

font.Dispose()

Catch ex As Exception

Me.familyName = System.Drawing.FontFamily.GenericSerif.Name

End Try

End Sub

 

' ====================================================================

' Cria uma imagem bitmap

' ====================================================================

Private Sub GeraImagem()

 

' Create a new 32-bit bitmap image.

Dim bitmap As New Bitmap(Me.m_width, Me.m_height, PixelFormat.Format32bppArgb)

 

' Create a graphics object for drawing.

Dim g As Graphics = Graphics.FromImage(bitmap)

g.SmoothingMode = SmoothingMode.AntiAlias

Dim rect As New Rectangle(0, 0, Me.m_width, Me.m_height)

 

' Fill in the background.

Dim hatchBrush As New HatchBrush(HatchStyle.SmallConfetti, Color.LightGray, Color.White)

g.FillRectangle(hatchBrush, rect)

 

' Set up the text font.

Dim size As SizeF

Dim fontSize As Single = rect.Height + 1

Dim font As Font

 

' Adjust the font size until the text fits within the image.

Do

fontSize -= 1

font = New Font(Me.familyName, fontSize, FontStyle.Bold)

size = g.MeasureString(Me.m_text, font)

Loop While size.Width > rect.Width

 

' Set up the text format.

Dim format As New StringFormat()

format.Alignment = StringAlignment.Center

format.LineAlignment = StringAlignment.Center

 

' Create a path using the text and warp it randomly.

Dim path As New GraphicsPath()

path.AddString(Me.m_text, font.FontFamily, CInt(font.Style), font.Size, rect, format)

Dim v As Single = 4.0F

Dim points As PointF() = {New PointF(Me.random.[Next](rect.Width) / v, Me.random.[Next](rect.Height) / v), New PointF(rect.Width - Me.random.[Next](rect.Width) / v, Me.random.[Next](rect.Height) / v), New PointF(Me.random.[Next](rect.Width) / v, rect.Height - Me.random.[Next](rect.Height) / v), New PointF(rect.Width - Me.random.[Next](rect.Width) / v, rect.Height - Me.random.[Next](rect.Height) / v)}

Dim matrix As New Matrix()

matrix.Translate(0.0F, 0.0F)

path.Warp(points, rect, matrix, WarpMode.Perspective, 0.0F)

 

' Draw the text.

hatchBrush = New HatchBrush(HatchStyle.LargeConfetti, Color.LightGray, Color.DarkGray)

g.FillPath(hatchBrush, path)

 

' Add some random noise.

Dim m As Integer = Math.Max(rect.Width, rect.Height)

For i As Integer = 0 To CInt((rect.Width * rect.Height / 30.0F)) - 1

Dim x As Integer = Me.random.[Next](rect.Width)

Dim y As Integer = Me.random.[Next](rect.Height)

Dim w As Integer = Me.random.[Next](m / 50)

Dim h As Integer = Me.random.[Next](m / 50)

g.FillEllipse(hatchBrush, x, y, w, h)

Next

 

' Clean up.

font.Dispose()

hatchBrush.Dispose()

g.Dispose()

' Set the image.

Me.m_image = bitmap

End Sub

End Class

End Namespace

 

O código tem o objetivo de gerar uma imagem bitmap.

Vamos incluir uma nova página chamada  JpegImage.aspx no web site através do menu Website opção Add new Item,e em Templates  selecione Web Form e informe o nome conforme figura abaixo;

No code-behind desta página , arquivo JpegImage.aspx.vb , inclua o código abaixo na página que irá gerar a imagem usando a classe criada no arquivo  Captcha.vb;

Imports System.Drawing

Imports System.Drawing.Imaging

 

Partial Class JpegImage

         Inherits System.Web.UI.Page

 

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

 

' Cria uma imagem CAPTCHA usando o texto armazenado na sessão

Dim ci As New Captcha.CaptchaImage(Me.Session("CaptchaImageText").ToString(), 200, 50, "Macoratti.net")

 

' altera a resposta do header para a saida de imagem JPEG

Me.Response.Clear()

Me.Response.ContentType = "image/jpeg"

' Escreve a imagem no stream de resposta no formato JPEG

ci.Image.Save(Me.Response.OutputStream, ImageFormat.Jpeg)

 

' libera o objeto imagem CAPTCHA.

ci.Dispose()

End Sub

End Class

 

A imagem é gerada usando o texto aleatório armazenado na sessão : Session("CaptchaImageText") e liberada no formato JPEG.

O texto aleatório será gerado na página onde a validação deve ser feita , no nosso caso a página Default.aspx.

Vejamos agora como deve ficar o código do arquivo Default.aspx.vb:

Partial Class _Default

    Inherits System.Web.UI.Page

 

' cria um codigo aleatorio que sera arrmazenado na sessão

Private random As New Random()

 

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

 

'Verifica se é a primeira execução da página

If Page.IsPostBack = False Then

     Me.Session("CaptchaImageText") = GeraCodigoAleatorio()

Else

    verificaCaptcha()

End If

End Sub
 

Private Function GeraCodigoAleatorio() As String


' Retorna uma string de sete digitos aleatorios

Dim s As String = ""

For i As Integer = 0 To 6

   s = [String].Concat(s, Me.random.[Next](10).ToString())

Next

Return s

End Function

 

Private Function verificaCaptcha() As Boolean

If Me.txtCodigoCaptcha.Text = Me.Session("CaptchaImageText").ToString() Then

   ' exibe uma mensagem de aviso

    Me.lblMensagem.Text = "C¢digo Correto."

   Return True

Else

   'exibe uma mensagem de erro

   Me.lblMensagem.Text = "C¢digo Incorreto."

'

   '  limpa a entra e cria um novo codigo aleaorio

     Me.txtCodigoCaptcha.Text = ""

      Me.Session("CaptchaImageText") = GeraCodigoAleatorio()

     Return False

End If

End Function

Protected Sub btnEnviar_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnEnviar.Click

If verificaCaptcha() Then

     Response.Redirect("ok.aspx")

       Exit Sub

Else

     Exit Sub

End If

End Sub

End Class

Vamos entender o que esta sendo feito:

O código -    Private random As New Random()   cria um código aleatório que será armazenado na sessão.

 

No evento Load do formulário temos que na primeira vez é gerado um código aleatório e colocado na sessão e nas demais vezes executamos a função verificaCaptcha();

 

'Verifica se é a primeira execução da página

If Page.IsPostBack = False Then

     Me.Session("CaptchaImageText") = GeraCodigoAleatorio()

Else

    verificaCaptcha()

End If

 

A função GeraCodigoAleatorio() usa o randômico gerado e retorna um string de 7 dígitos aleatórios;

 

Private Function GeraCodigoAleatorio() As String


' Retorna uma string de sete digitos aleatorios

Dim s As String = ""

For i As Integer = 0 To 6

   s = [String].Concat(s, Me.random.[Next](10).ToString())

Next

Return s

End Function

 

A função verificaCaptcha() ir verificar se o código digitado na caixa de texto - txtCodigoCaptcha - é igual ao gerado na sessão se não for igual exibe uma mensagem , limpa o TextBox e gera outro código na sessão.

 

Private Function verificaCaptcha() As Boolean

If Me.txtCodigoCaptcha.Text = Me.Session("CaptchaImageText").ToString() Then

   ' exibe uma mensagem de aviso

    Me.lblMensagem.Text = "C¢digo Correto."

   Return True

Else

   'exibe uma mensagem de erro

   Me.lblMensagem.Text = "C¢digo Incorreto."

'

   '  limpa a entra e cria um novo codigo aleaorio

     Me.txtCodigoCaptcha.Text = ""

      Me.Session("CaptchaImageText") = GeraCodigoAleatorio()

     Return False

End If

End Function

 

E como a imagem é gerada na página ???

 

Note que introduzimos o controle :  <img src="JpegImage.aspx" />

 

Neste controle a imagem é obtida como retorno da página JpegImage.aspx que gera a imagem a partir do texto na sessão usando a classe Captcha que criamos.

Falta apenas criar uma página chamada ok.aspx para a qual o usuário será direcionado caso o código esteja correto.

No menu WebSite selecione Add New Item e inclua um novo WebForm com o nome de ok.aspx a seguir defina o seguinte leiaute para a página:

O código para validação e verificação do Captcha é feito no botão Enviar. No seu evento Click coloque o código:

Protected Sub btnEnviar_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnEnviar.Click

If verificaCaptcha() Then

      Response.Redirect("ok.aspx")

         Exit Sub

Else

        Exit Sub

End If

End Sub

Executando a página iremos obter:

Este artigo teve o objetivo de introduzir o assunto apresentando uma forma de implementar a validação por imagens em páginas ASP .NET e não deve ser tomado como referência exclusiva. Se você pretende implementar a técnica em um site de produção deverá usar um código mais robusto e pesquisar melhores opções existentes.

Lembrando que usar CAPTCHA por si só não evita um ataque, visto que já existem robôs que quebram o CAPTCHA  usando algoritmos complexos e técnicas de reaproveitamento da "session-ID" de um captcha conhecido.

Veja este texto publicado no  GLOBO - Informática  por Carlos Alberto Teixeira :

Hoje em dia, encontrar um captcha não é nada difícil. Só que já tem uma rapaziada derrotando captcha. Acredite, tem sim. São brilhantes esses caras. Eles estão construindo bots que capturam um captcha original em um site X, apresentam-no a um humano num site diferente Y, em outro contexto, registram a resposta da criatura e submetem-na de volta ao campo digitado no site X de onde capturaram a figura, transpondo a barreira de segurança e ganhando acesso a seja lá o que for que estão querendo acessar.

Em geral, esses gênios usam como site Y páginas de pornografia, que os tarados virtuais acessam em busca de material para seus devaneios. Assim, fingindo que é uma barreira real, o bot apresenta ao ávido internauta o captcha capturado no site X. O camarada vai lá e digita sua interpretação da figura. O bot pega os caracteres digitados no site pornô Y, que está sob seu controle, e "digita" os mesmos caracteres no site X. E pronto, mais uma vitória da inteligência a serviço do mal.

Então todo o cuidado é pouco...

Até o próximo artigo ASP .NET. 

Veja o projeto funcionando aqui : http://macoratti.net/captcha

Veja os Destaques e novidades do SUPER DVD Visual Basic (sempre atualizado) : clique e confira !

Quer migrar para o VB .NET ?

Quer aprender C# ??

Quer aprender os conceitos da Programação Orientada a objetos ?

Quer aprender o gerar relatórios com o ReportViewer no VS 2013 ?

Referências:

 


José Carlos Macoratti