CAPTCHAS (Parte 1) - Aspectos de Segurança e Técnicas de Utilização

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.

Um pouco de curiosidade Histórica

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.

Início da páginaInício da página

O que é CAPTCHA

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:

Início da páginaInício da página

A fragilidade dos sistemas na WEB

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 um determinado site é necessário criarmos uma votação on-line onde desejamos capturar o voto do usuário sobre uma determina questão. A identidade do usuário não é necessária, o resultado esta baseado na visita do usuário controlada por cookie. Embora não seja possível a invasão o resultado de uma votação pode ser alterado com um pequeno programa de simulação, alterando resultado de uma enquete.

- É comum vermos em alguns sites a opção de inclusão em lista de envio com a simples digitação de um e-mail. Pelo mesmo processo de simulação poderíamos poluir a lista com diversos e-mails falsos gerando transtornos ao processo e aos usuários detentores do e-mail, pois fariam parte de uma lista que não escolheram e para o site, que passaria a ser visto com emissor de emails não autorizados.

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).

Início da páginaInício da página

Login e Senhas X Ataques de Dicionários

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.

Início da páginaInício da página

Criando um Namespace RTT

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:

Gerar dados aleatórios

Gerar imagens com fundo variável

Gerar Texto aleatório

Geração da Imagem com distorção

Início da páginaInício da página

Referencias a Namespaces e enumerção

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
	
Início da páginaInício da página

Gerar dados aleatórios de forma segura

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:

System.Random, que gera um "pseudo-random number" baseia em uma seqüência finita baseado em algoritmo matemático.

System.Security.Cryptography.RandomNumberGenerator indicada para geração de senhas e algoritmos de segurança usando cryptographic service providers, (Será esta que usaremos).

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).

Início da páginaInício da página

Gerar imagens com fundo variável

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:

HatchBrush- gera um padrão de preenchimento sobre uma figura

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:

http://msdn.microsoft.com/library/default.asp?url=/library/en-s/cpref/html/frlrfsystemdrawingdrawing2dhatchbrushmemberstopic.asp

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemdrawingdrawing2dlineargradientbrushmemberstopic.asp

Início da páginaInício da página

Gerar Texto aleatório

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.

Início da páginaInício da página

Geração da imagem com distorção

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

	
Início da páginaInício da página

Exemplo de Aplicação do Namespace RTT

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

	

Início da páginaInício da página

Código Fonte do Namespace RTT e Demo

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.

Início da páginaInício da página

Referências

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


Início da páginaInício da página