By Fernando Cerqueira (fernandocerqueira@msn.com)
Microsoft MVP ASP.NET
Novembro 2004
"Legal, temos uma aplicação web que previne o "sql-inject", ataques de negação, senhas encriptografadas e salvas no registro e protegias por ACL, etc.... Ótimo, o aplicativo esta bem protegido, mas já verificou se os processos estão preparados para evitar a intervenção não de uma pessoa mas de uma máquina....?"
Quando desenvolvemos aplicativos para web, estamos sujeitos a ataques te todas as naturezas, muitas delas provenientes de falhas de segurança em nosso código e outros por falha em nossos processos!
O objetivo deste artigo (parte1) é apresentar uma das técnicas de segurança para evitar ataques provenientes de máquinas e não e Humanos nos processos de interação com o usuário:
CAPTCHA
Completely Automated Public Turing test to tell Computers and Humans Apart.
Na parte 2 do artigo será mostrado a criação de um Server Control para ASP.NET para gerar um captcha com autenticação.
A origem do questionamento do uso da máquina com inteligência data de 1950 quando Alan Turing apresentou um jogo: "the imitation game" e o seguinte questionamento: "Uma Máquina pode pensar?". Os testes e analises de Turing foram a base da AI (Artifical Inteligence) que hoje conhecemos. O jogo consistia em 3 personagens:
(A) Um Homem, (B) uma Mulher e (C) Um interrogador de qualquer um dos sexos. O Interrogador fica em uma sala separada e terá como objetivo determinar qual dos personagens é uma mulher, por sua vez cada um dos personagens (A) e (B) irão tentar convencer que ele (A) ou (B) é uma mulher. Depois o teste foi feito substituindo um dos personagens por uma máquina. As conclusões e analise podem ser vista um documento (http://crl.ucsd.edu/~saygin/papers/MMTT.pdf).
Os testes de Alan Turing visavam diferenciar para um humano quem era a máquina ou não. Para o cenário do desenvolvedor o teste é reverso, ou seja, precisamos criar testes que diferenciem humanos de máquinas para uma máquina, estes testes são chamados de Reverse Turing Test ou RTT.
A aplicação deste conceito em sites populares se deu em 1997 quando o site de busca Alta Vista passou a usar RTT para evitar ataques de "span-add-URL" apresentando uma imagem com texto randômico. A redução dos ataques foi reduzida em 95%. Outro exemplo foi o site Yahoo que no ano de 2000 estava com problemas em suas salas de bate-papo - "Chart Room", devido ao excesso de robots, "poluindo" com anúncios e propagandas. Para minimizar este problema foi colocada junto ao login também uma imagem distorcida com um texto a ser digitado pelo usuário reduzindo drasticamente também as incidências destes casos. Perceba que não usamos a palavra eliminada, pois o RTT não elimina a possibilidade de uma máquina se passar por humano e sim dificulta em vários níveis esta possibilidade.
Captcha, de um modo geral, é um teste que requer uma resposta a ser informada, onde os humanos têm facilidade em responder e as máquina não. As formas mais populares de captcha são textos distorcidos, "mesclados" ou obscurecidos, sobre um fundo que dificulta o entendimento do texto, sem perder o sentido da palavra. Outras formas de Captcha são imagens conhecidas (ex: um carro) onde o usuário é solicitado responder o que é ou representa aquela imagem, ou ainda para os deficientes visuais, o envio de sons soletrados para serem digitados.
Exemplos:


O uso de captcha, ou processos similares, deve ser levando em conta quando queremos garantir uma segurança maior em nossos aplicativos, evitando ataques de máquinas que podem simular a presença humana. Para nos situamos no uso prático de captcha daremos 2 exemplos simples, que a falta desta técnica ou outras semelhantes, podem gerar problemas para os aplicativos tornando sistemas vulneráveis a ataques:
Em ambos os exemplos está se partindo da premissa que o aplicativo tem todas as seguranças contra as invasões. Em ambos os casos, embora possam estar devidamente protegidos, os processos não foram analisados sobre outra ótica: A intervenção de uma máquina simulando a presença Humana, abrindo "brechas" de segurança por simulação de entrada de dados feitos por máquinas (programas).
Na grande maioria dos processos de segurança, a passagem autenticação é feita pela dupla inseparável login e senha. Neste ponto existe a maior chance de vulnerabilidade de um ataque, em especial os de programas com "dictionary attacks". Sobre o ponto de vista do servidor as soluções são feitas utilizando-se softwares (algoritmos de criptografia e segurança Ex: MD5 e ACL) e infra-estrutura de hardware (Firewall, Portas fechadas, etc) porem se olharmos do ponto de vista do usuário, basta ser informando uma palavra chave (senha) associada a um login. Esta combinação necessária abre espaço pelo lado do usuário a simular, repetidas vezes, diversas combinações possíveis com o intuito de obter uma combinação válida para o servidor, principalmente quando se tem a informação de um de seus elementos (normalmente o login) que em 99% dos casos é visualizado ou de domínio publico. O login em sua grande maioria é digitado e apresentado sem criptografia ou sobreposição de caracteres, logo quem estiver perto poderá ver. Outro caso comum em web sites é o uso do e-mail como login associado a uma senha. Um estudo deste tipo de ataque é detalhado em http://ww.pinkas.net/papers/pwdweb.pdf .
O uso de RTT (Reverse Turing Test) durante o processo de autenticação, combinado com outros recursos como limite de tempo e tentativas podem elevar o nível de segurança nestes processos de entrada e outros processos que necessitem de garantia maior a ataques simulações de entrada feito por programas.
O.Net Framework vem com diversas classes que nos ajudam a implementar segurança, mas nenhuma delas aplicando o conceito de RTT. Para a construção do namespace RTT iremos atender aos seguintes critérios:
Em nosso namespace RTT fazemos referência a outros namespaces usados neste projeto, em especial o que facilita a manipulação de imagens em .NET. Outros namespaces usados são de segurança e o de manipulação de texto para geração da string. Visando a manutenção e legibilidade do código foi criando também uma enumeração com os valores default de inicialização e de valores máximos e mínimos que serão utilizados nas classes.
Namaspaces referenciados no RTT
System.Drawing System.Drawing.Drawing2D System.Security.Cryptography System.Text.ASCIIEncoding
Enumeração usada no RTT
Public Enum RttValues DefaultStartLeft = 5 DefaultStartTop = 15 DefaultWidthImage = 200 DefaultHeightImage = 80 DefaultMinValueSizeFont = 26 DefaultMaxValueSizeFont = 30 DefaultMaxValueTextLen = 5 MinValueTextLen = 4 MaxValueRuido = 5 MinValueRuido = 3 MaxValueDist = 64 MinValueDist = 45 MinValueRGB = 0 MaxValueRGB = 255 End Enum
Para termos os valores aleatórios criaremos uma classe de geração de números aleatórios. Podemos usar 2 formas de gerar números aleatórios:
Vejamos o código da classe responsável por esta atividade:
Namespace RTT
Public Class RttRandonNumber
Private ArrayByteIntRnd(4) As Byte
Private Rng As RNGCryptoServiceProvider
Public Sub New()
Rng = New RNGCryptoServiceProvider
End Sub
Public Function NewNumber(ByVal Max) As Int32
Rng.GetBytes(ArrayByteIntRnd)
Dim Ret As Integer = BitConverter.ToInt32(ArrayByteIntRnd, 0) Mod Max
If Ret < 0 Then
Ret *= -1
End If
Return Ret
End Function
Public Function NewNumber(ByVal MaxVal As Integer, ByVal MinVal As Integer) As Int32
Rng.GetBytes(ArrayByteIntRnd)
Dim Ret As Integer = BitConverter.ToInt32(ArrayByteIntRnd, 0) Mod (MaxVal - MinVal + 1)
If Ret < 0 Then
Ret *= -1
End If
Return Ret + MinVal
End Function
End Class
End Namespace
Para gerar valores dentro um ranger usa-se a técnica de módulo X (resto da divisão por uma base).
O fundo de uma imagem pode ser criado de varias formas. Uma delas é usando padrões de "pinceis", alterando cores, usando padrões de preenchimento etc. O namespece System.Drawing.Drawing2D possui uma seria de facilidades para se trabalhar com gráficos vetorizados e opções de pinceis para preenchimento , usaremos para nosso namespace RTT a forma abaixo:
A classe que encapsula a chamada a este padrão e adicionar as opções de escolher aleatoriamente suas características, esta logo abaixo:
Namespace RTT
Public Class RttImageBackground
Private _StyleHatchBrush As HatchStyle
Private _ForeColorHatchBrush As Color
Private _BackColorHatchBrush As Color
Private _MinValueRGBForeColor As Integer
Private _MaxValueRGBForeColor As Integer
Private _MinValueRGBBackColor As Integer
Private _MaxValueRGBBackColor As Integer
Public Sub New()
_MinValueRGBForeColor = RttValues.MinValueRGB
_MaxValueRGBForeColor = RttValues.MaxValueRGB
_MinValueRGBBackColor = RttValues.MinValueRGB
_MaxValueRGBBackColor = RttValues.MaxValueRGB
RandonStyle()
End Sub
Public Property MinValueRGBForeColor() As Integer
Get
Return _MinValueRGBForeColor
End Get
Set(ByVal Value As Integer)
If Value < RttValues.MinValueRGB Then
Throw New System.Exception("Valor menor que o Mínimo - MinValueRGBForeColor")
End If
_MinValueRGBForeColor = Value
If _MinValueRGBForeColor > _MaxValueRGBForeColor Then
Throw New System.Exception("Valor Mínimo maior que o Máximo - MinValueRGBForeColor")
End If
End Set
End Property
Public Property MaxValueRGBForeColor() As Integer
Get
Return _MaxValueRGBForeColor
End Get
Set(ByVal Value As Integer)
If Value > RttValues.MaxValueRGB Then
Throw New System.Exception("Valor maior que o Máximo - MaxValueRGBForeColor")
End If
_MaxValueRGBForeColor = Value
If _MinValueRGBForeColor > _MaxValueRGBForeColor Then
Throw New System.Exception("Valor Máximo omenor que o Mínimo - MaxValueRGBForeColor")
End If
End Set
End Property
Public Property MinValueRGBBackColor() As Integer
Get
Return _MinValueRGBBackColor
End Get
Set(ByVal Value As Integer)
If Value < RttValues.MinValueRGB Then
Throw New System.Exception("Valor menor que o Mínimo - MinValueRGBBackColor")
End If
_MinValueRGBBackColor = Value
If _MinValueRGBBackColor > _MaxValueRGBBackColor Then
Throw New System.Exception("Valor Mínimo maior que o Máximo - MinValueRGBBackColor")
End If
End Set
End Property
Public Property MaxValueRGBBackColor() As Integer
Get
Return _MaxValueRGBBackColor
End Get
Set(ByVal Value As Integer)
If Value > RttValues.MaxValueRGB Then
Throw New System.Exception("Valor maior que o Máximo - MaxValueRGBBackColor")
End If
_MaxValueRGBBackColor = Value
If _MinValueRGBBackColor > _MaxValueRGBBackColor Then
Throw New System.Exception("Valor Máximo menor que o Mínimo - MaxValueRGBBackColor")
End If
End Set
End Property
Public Property StyleHatchBrush() As HatchStyle
Get
Return _StyleHatchBrush
End Get
Set(ByVal Value As HatchStyle)
_StyleHatchBrush = HatchStyle.BackwardDiagonal
End Set
End Property
Public Property ForeColorHatchBrush() As Color
Get
Return _ForeColorHatchBrush
End Get
Set(ByVal Value As Color)
_ForeColorHatchBrush = Value
End Set
End Property
Public Property BackColorHatchBrush() As Color
Get
Return _BackColorHatchBrush
End Get
Set(ByVal Value As Color)
_BackColorHatchBrush = Value
End Set
End Property
Public Sub RandonStyle()
Dim Rnd As New RttRandonNumber
_StyleHatchBrush = Rnd.NewNumber(52)
_ForeColorHatchBrush = System.Drawing.Color.FromArgb( _
Rnd.NewNumber(_MaxValueRGBForeColor, _MinValueRGBForeColor), _
Rnd.NewNumber(_MaxValueRGBForeColor, _MinValueRGBForeColor), _
Rnd.NewNumber(_MaxValueRGBForeColor, _MinValueRGBForeColor))
_BackColorHatchBrush = System.Drawing.Color.FromArgb( _
Rnd.NewNumber(_MaxValueRGBBackColor, _MinValueRGBBackColor), _
Rnd.NewNumber(_MaxValueRGBBackColor, _MinValueRGBBackColor), _
Rnd.NewNumber(_MaxValueRGBBackColor, _MinValueRGBBackColor))
End Sub
End Class
End Namespace
Existem vários padrões de preenchimento, alem deste escolhido. Para cada padrão podemos definir as cores e característica que podem ser customizadas, Como fonte inicial de pesquisa pode-se ver mais detalhes em 2 links abaixo:
Sobre a imagem com o fundo já preenchido, devera ser colocado o texto que será usando como base da "pergunta". O texto a ser gerado poderá ser definido com um mínimo e máximo de caracteres (letras e números) alterando-se as fontes, cores e tamanhos. "Para definição das fontes usamos uma propriedade texto, onde será armazenado as fontes possíveis separadas por um delimitador: ; (Ponto e virgula) . Os Tamanho possíveis serão definidos por 2 propriedades limitando o mínimo e máximo e a mudança de cores por um "flag" que definira a ativação desta funcionalidade. Vejamos então:
Namespace RTT
Public Class RttImageText
Public Const DefaultFonts As String = "Arial;Georgia;Microsoft Sans Serif"
Private _Text As String
Private _MaxLen As Integer
Private _MinLen As Integer
Private _ArrayFnt As String()
Private _fontes As String
Private _MinSizeFont As Integer
Private _MaxSizeFont As Integer
Private _MultColor As Boolean
Private _ColorText As Color
Public Property MinSizeFont() As Integer
Get
Return _MinSizeFont
End Get
Set(ByVal Value As Integer)
_MinSizeFont = Value
If _MaxSizeFont < _MinSizeFont Then
Throw New System.Exception("Valor Mínimo maior que o Máximo - MinSizeFont")
End If
End Set
End Property
Public Property MaxSizeFont() As Integer
Get
Return _MaxSizeFont
End Get
Set(ByVal Value As Integer)
_MaxSizeFont = Value
If _MaxSizeFont < _MinSizeFont Then
Throw New System.Exception("Valor Máximo menor que o Mínimo - MaxSizeFont")
End If
End Set
End Property
Public Sub NewRandColorText()
Dim Rnd As New RttRandonNumber
_ColorText = System.Drawing.Color.FromArgb( _
Rnd.NewNumber(RttValues.MaxValueRGB), _
Rnd.NewNumber(RttValues.MaxValueRGB), _
Rnd.NewNumber(RttValues.MaxValueRGB))
End Sub
Public Sub NewRandText()
Dim Rnd As New RttRandonNumber
Dim Ind As Integer
Dim Max As Integer = Rnd.NewNumber(_MaxLen, _MinLen)
Dim bytes(Max - 1) As Byte
_Text = ""
For Ind = 1 To Max
Dim ok As Boolean
Dim Ascii As Integer
Do
Ascii = Rnd.NewNumber(122, 48)
If Ascii >= 58 And Ascii <= 64 Then
ok = False
ElseIf Ascii >= 91 And Ascii <= 96 Then
ok = False
Else
ok = True
End If
Loop While Not ok
bytes(Ind - 1) = Ascii
Next
_Text = Ascii.GetString(bytes)
End Sub
Public Sub New()
_MinSizeFont = RttValues.DefaultMinValueSizeFont
_MaxSizeFont = RttValues.DefaultMaxValueSizeFont
_MaxLen = RttValues.DefaultMaxValueTextLen
_MinLen = RttValues.MinValueTextLen
_fontes = DefaultFonts
_ArrayFnt = _fontes.Split(";")
_ColorText = Color.FromArgb(RttValues.MinValueRGB, RttValues.MinValueRGB, RttValues.MinValueRGB)
_MultColor = True
NewRandText()
End Sub
Public Function GetFontCount() As Integer
Return _ArrayFnt.GetUpperBound(0)
End Function
Public Function GetFont() As String
Dim Rnd As New RttRandonNumber
Return _ArrayFnt(Rnd.NewNumber(_ArrayFnt.GetUpperBound(0), _ArrayFnt.GetLowerBound(0)))
End Function
Public Function GetFont(ByVal value As Integer) As String
Return _ArrayFnt(value)
End Function
Public Property fontes() As String
Get
Return _fontes
End Get
Set(ByVal Value As String)
If Value = "" Then
Value = DefaultFonts
End If
_fontes = Value
_ArrayFnt = _fontes.Split(";")
End Set
End Property
Public Property Texto() As String
Get
Return _Text
End Get
Set(ByVal Value As String)
_Text = Value
End Set
End Property
Public Property MultColor() As Boolean
Get
Return _MultColor
End Get
Set(ByVal Value As Boolean)
_MultColor = Value
End Set
End Property
Public Property ColorText() As Color
Get
Return _ColorText
End Get
Set(ByVal Value As Color)
_ColorText = Value
End Set
End Property
Public Property MaxText() As Integer
Get
Return _MaxLen
End Get
Set(ByVal Value As Integer)
If Value < 0 Then
Value = 0
End If
_MaxLen = Value
If _MaxLen < _MinLen Then
Throw New System.Exception("Valor Máximo menor que o Mínimo - MaxText")
End If
End Set
End Property
Public Property MinText() As Integer
Get
Return _MinLen
End Get
Set(ByVal Value As Integer)
If Value < RttValues.MinValueTextLen Then
Throw New System.Exception("Valor menor que o Mínimo - MinText")
End If
_MinLen = Value
If _MaxLen < _MinLen Then
Throw New System.Exception("Valor Mínimo maior que o Máximo - MinText")
End If
End Set
End Property
End Class
End Namespace
Para garantir apenas um resultado de letras e números usamos o intervalo da tabela ASCII de 48 a 122 (decimal) e excluímos os caracteres especiais dentro deste intervalo no método NewRandText que é responsável pela geração aleatória do texto a ser apresentado. Um ótimo link para consulta das tabelas ASCII e UNICODE pode ser visto em: http://www.asciitable.com.
A distorção da imagem será feira aplicando-se sobre a imagem a função senoidal incluindo um pequeno "ruído" ao não se aplicar a função em todos os pontos da imagem. O código da classe esta abaixo:
Namespace RTT
Public Class ImageCaptacha
Private _height As Integer
Private _width As Integer
Private _backgroud As RttImageBackground
Private _texto As RttImageText
Private _StartTop As Integer
Private _Startleft As Integer
Private _MinDist As Integer
Private _MaxDist As Integer
Private _MinRuido As Integer
Private _MaxRuido As Integer
Public Sub New()
_Startleft = RttValues.DefaultStartLeft
_StartTop = RttValues.DefaultStartTop
_width = RttValues.DefaultWidthImage
_height = RttValues.DefaultHeightImage
_backgroud = New RttImageBackground
_texto = New RttImageText
_MinRuido = RttValues.MinValueRuido
_MaxRuido = RttValues.MaxValueRuido
_MinDist = RttValues.MinValueDist
_MaxDist = RttValues.MaxValueDist
End Sub
Public Property StartTop() As Integer
Get
Return _StartTop
End Get
Set(ByVal Value As Integer)
_StartTop = Value
End Set
End Property
Public Property StartLeft() As Integer
Get
Return _Startleft
End Get
Set(ByVal Value As Integer)
_Startleft = Value
End Set
End Property
Public Property MinRuido() As Integer
Get
Return _MinRuido
End Get
Set(ByVal Value As Integer)
If Value < RttValues.MinValueRuido Then
Throw New System.Exception("Valor menor que Mínino - MinRuido")
End If
_MinRuido = Value
If _MaxRuido < _MinRuido Then
Throw New System.Exception("Valor Mínino maior que Máximo - MinRuido")
End If
End Set
End Property
Public Property MaxRuido() As Integer
Get
Return _MaxRuido
End Get
Set(ByVal Value As Integer)
If Value > RttValues.MaxValueRuido Then
Throw New System.Exception("Valor maior que Máximo - MaxRuido")
End If
_MaxRuido = Value
If _MaxRuido < _MinRuido Then
Throw New System.Exception("Valor Máximo menor que Mínino - MaxRuido")
End If
End Set
End Property
Public Property MaxDist() As Integer
Get
Return _MaxDist
End Get
Set(ByVal Value As Integer)
If Value > RttValues.MaxValueDist Then
Throw New System.Exception("Valor maior que Máximo - MaxDist")
End If
_MaxDist = Value
If _MaxDist < _MinDist Then
Throw New System.Exception("Valor Máximo menor que Mínino - MaxDist")
End If
End Set
End Property
Public Property MinDist() As Integer
Get
Return _MinDist
End Get
Set(ByVal Value As Integer)
If Value < RttValues.MinValueDist Then
Throw New System.Exception("Valor menor que Mínimo - MinDist")
End If
_MinDist = Value
If _MaxDist < _MinDist Then
Throw New System.Exception("Valor Mínino maior que o Máximo - MinDist")
End If
End Set
End Property
Public Function NewImage() As Bitmap
Return GeraNewImage(_width, _height)
End Function
Public Function NewImage(ByVal Width As Integer, ByVal height As Integer) As Bitmap
_width = Width
_height = height
Return GeraNewImage(_width, _height)
End Function
Private Function GeraNewImage(ByVal Width As Integer, ByVal height As Integer) As Bitmap
Dim objBitmap As Bitmap
Dim objGraphics As Graphics
Dim objBrush As HatchBrush
objBitmap = New Bitmap(_width, _height)
objGraphics = Graphics.FromImage(objBitmap)
objBrush = New HatchBrush(_backgroud.StyleHatchBrush, _
_backgroud.ForeColorHatchBrush, _backgroud.BackColorHatchBrush)
objGraphics.FillRectangle(objBrush, 0, 0, _width, _height)
Dim objFont As Font
Dim myFont As String
Dim mysize As Integer
Dim Ind As Integer
Dim Acu As Integer = _Startleft
Dim Rnd As New RttRandonNumber
For Ind = 0 To _texto.Texto.Length - 1
myFont = _texto.GetFont()
mysize = Rnd.NewNumber(_texto.MaxSizeFont, _texto.MinSizeFont)
objFont = New Font(myFont, mysize, New FontStyle().Bold)
Dim drawBrush As SolidBrush
If _texto.MultColor Then
_texto.NewRandColorText()
End If
drawBrush = New SolidBrush(_texto.ColorText)
objGraphics.DrawString(_texto.Texto.Substring(Ind, 1), objFont, drawBrush, Acu, _StartTop)
Acu += objGraphics.MeasureString(_texto.Texto.Substring(Ind, 1), objFont).Width
Next
Dim x1 As Integer
Dim Y1 As Integer
Dim OriImg As Bitmap = objBitmap.Clone
Dim Angulo As Integer = Rnd.NewNumber(_MaxDist, _MinDist)
For Y1 = 0 To _height - 1
Dim Dist As Integer = Rnd.NewNumber(_MaxRuido, _MinRuido)
For x1 = 0 To _width - 1
Dim NEWX As Integer = (x1 + (Dist * Math.Cos(Math.PI * Y1 / Angulo)))
Dim NEWY As Integer = (Y1 + (Dist * Math.Sin(Math.PI * x1 / Angulo)))
If NEWX < 0 Or NEWX >= _width Then
NEWX = 0
End If
If NEWY < 0 Or NEWY >= _height Then
NEWY = 0
End If
objBitmap.SetPixel(x1, Y1, OriImg.GetPixel(NEWX, NEWY))
Next
Next
objGraphics.Flush()
Return objBitmap
End Function
Public Property ImageBackgroud() As RttImageBackground
Get
Return _backgroud
End Get
Set(ByVal Value As RttImageBackground)
_backgroud = Value
End Set
End Property
Public Property Imagetext() As RttImageText
Get
Return _texto
End Get
Set(ByVal Value As RttImageText)
_texto = Value
End Set
End Property
Public Property Width() As Integer
Get
Return _width
End Get
Set(ByVal Value As Integer)
_width = Value
End Set
End Property
Public Property Height() As Integer
Get
Return _height
End Get
Set(ByVal Value As Integer)
_height = Value
End Set
End Property
End Class
End Namespace
O uso do namespace RTT reduz drasticamente o código (bastando apenas 2 linhas para se gerar uma imagem. Na segunda parte do artigo com a criação de um server control com autenticação será demonstrado maneiras de se autenticar os dados digitados, uma delas, bem simples é armazenar em session o valor do texto a ser digitado para uma autenticação.
No exemplo abaixo demonstra o uso do namespace gerando a imagem pelos valores default das classes, podendo o desenvolvedor, ajustarem as propriedades para suas necessidades e aplicações. O exemplo possui 2 forms: webform1 que apresenta e valida o texto apresentado pela imagem e webform2 responsável pela geração da imagem e persistência do valor em session.
Imports RTT
Imports System.Drawing.Imaging
Public Class WebForm2
Inherits System.Web.UI.Page
#Region " Web Form Designer Generated Code "
'This call is required by the Web Form Designer.
<System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
End Sub
'NOTE: The following placeholder declaration is required by the Web Form Designer.
'Do not delete or move it.
Private designerPlaceholderDeclaration As System.Object
Private Sub Page_Init(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Init
'CODEGEN: This method call is required by the Web Form Designer
'Do not modify it using the code editor.
InitializeComponent()
End Sub
#End Region
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
'Put user code to initialize the page here
Dim ObjImg As New ImageCaptacha
Session(Session.SessionID) = ObjImg.Imagetext.Texto
ObjImg.NewImage().Save(Response.OutputStream, ImageFormat.Jpeg)
End Sub
End Class
Imports System.Drawing.Imaging
Imports RTT
Public Class WebForm1
Inherits System.Web.UI.Page
#Region " Web Form Designer Generated Code "
'This call is required by the Web Form Designer.
<System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
End Sub
Protected WithEvents Image1 As System.Web.UI.WebControls.Image
Protected WithEvents TextBox1 As System.Web.UI.WebControls.TextBox
Protected WithEvents Button1 As System.Web.UI.WebControls.Button
Protected WithEvents Label1 As System.Web.UI.WebControls.Label
Protected WithEvents LinkButton1 As System.Web.UI.WebControls.LinkButton
'NOTE: The following placeholder declaration is required by the Web Form Designer.
'Do not delete or move it.
Private designerPlaceholderDeclaration As System.Object
Private Sub Page_Init(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Init
'CODEGEN: This method call is required by the Web Form Designer
'Do not modify it using the code editor.
InitializeComponent()
End Sub
#End Region
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
If TextBox1.Text.ToUpper = Session(Session.SessionID).ToUpper Then
Label1.Text = "Valor digitado OK"
Else
Label1.Text = "Valor digitado Não confere"
End If
TextBox1.Text = ""
Session(Session.SessionID) = Nothing
End Sub
Private Sub LinkButton1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles LinkButton1.Click
TextBox1.Text = ""
Label1.Text = ""
Session(Session.SessionID) = Nothing
End Sub
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Image1.ImageUrl = "webform2.aspx"
End Sub
Private Sub Page_Unload(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Unload
Session(Session.SessionID) = Nothing
End Sub
End Class


Clique aqui para fazer o download do arquivo fonte do namespace RTT com todas as classes e o demo acima. Para testar crie um projeto web application e adicione os 2 arquivos webform1 e webform2. Depois adicione o projeto RTTCaptacha.vbproj a sua solução e faça a referencia deste projeto em sua aplicação.
Turing Machine - Alan Turing
http://mathworld.wolfram.com/TuringMachine.html
Inaccessibility of Visually-Oriented Anti-Robot Tests
http://www.w3.org/TR/turingtest/
Breaking a Visual CAPTCHA
http://www.cs.berkeley.edu/~mori/gimpy/gimpy.html
Berkeley - Compute Vision Group
http://http.cs.berkeley.edu/projects/vision/vision_group.html
Projeto Captcha.net
http://www.captcha.net/
Securing Passwords Againts Dictionary Attacks (Benny Pinkas & Tomas Sanders)
http://www.pinkas.net/PAPERS/pwdweb.pdf