Quebrando CAPTCHAs

CAPTCHAs (Completely Automated Public Turing test to tell Computers and Humans Apart“) são testes de Turing automatizados que tentam descobrir se você é um ser humano ou uma máquina. Em síntese os CAPTCHAs são aquelas imagenzinhas embaralhadas que aparecem quando você vai fazer um post em um blog, fazer consulta ou cadastro em alguns sites.

Por que utilizar? Ao utilizar um CAPTCHA em seu site, você evita que programas enviem spams, evita cadastros indevidos e tenta diminuir o uso informações automatizadas em outros sites. Um exemplo de uso é o meu formulário de contato.

Será que o CAPTCHA é realmente uma forma eficaz de descobrir se realmente é um ser humano que está realizando a operação? Se a gente consegue identificar o CAPTCHA, por que uma máquina não conseguiria? Um dos alunos meu perguntou se haveria uma forma de se realizar isso. A escolha foi o CAPTCHA da Receita Federal: http://www.receita.fazenda.gov.br/PessoaJuridica/CNPJ/cnpjreva/Cnpjreva_Solicitacao.asp

Por questões de segurança não irei disponibilizar o código, mas para fins acadêmicos, demonstrarei que é possível ultrapassar a barreira de alguns CAPTCHA. Alguns são fáceis, outros nem tanto.

Por exemplo, vamos ver o CAPTCHA do Facebook:

Captcha Facebook

Captcha Facebook

Esse CAPTCHA é monocromático, o que facilita um pouco e os ruídos existentes são uma bola de fundo e o texto torto. O do Orkut/Google só tem o texto torto:

Captcha Orkut

Captcha Orkut

Vamos ao da Receita Federal:

Captcha Receita Federal do Brasil

Captcha Receita Federal do Brasil

Esse CAPTCHA é muito mais complicado, há uma imagem de fundo que complica bastante, o texto está torto e são gerados vários tipos de CAPTCHAs diferentes, um com texto pontilhado, outro com um filtro esférico e esse da imagem com o texto torto em dois eixos. Vamos ao mais difícil. Fiz um programinha pra automatizar a tarefa.

O primeiro passo que eu fiz foi obter a imagem do CAPTCHA. Essa etapa é a mais fácil, o código para obter foi o seguinte:

java.awt.Toolkit.getDefaultToolkit()
.createImage(new URL("http://www.receita.fazenda.gov.br/scripts/srf/intercepta/captcha.aspx?opt=image"));

Com essa imagem eu converto para uma BufferedImage e consigo pegar os valores de cada pixel pelo método getRGB(x,y) e posso montar minha matriz. Agora é só relembrar da época da Graduação e Mestrado: Geometria Analítica, Cálculo Numérico, Álgebra Linear, Computação Gráfica, Extração de Características, … No meu Mestrado trabalhei bastante com extração de características de imagens médicas: forma, cor, textura, identificação de cluster, … os conceitos são os mesmo, as características que mudam.

Os passos que executei para o texto torto e fonte forte foram:

  • Utilizar um filtro de eliminação de ruídos. Implementei um filtro de mediana.
  • Reduzir o espaço de cores da imagem.
  • Utilizar uma transformação para “endireitar a imagem”. No caso utilizei duas funções cosseno. y=a*cos(b*x)+c e x=d*cos(e*f)+g. O mais difícil foi identificar os coeficientes. Fui na tentativa e erro. Ainda não está 100%.
  • Dividir os caracteres. Poderia ser utilizado um k-means com k=4. Já que sempre são 4 letras.
  • Extrair os caracteres e utilizar para treinamento no OCR. Realizar essas etapas com um número bom de letras. Obter o alfabeto todo seria o ideal.
  • Aplicar o OCR identificando o texto.

Segue o programinha:

Convert Image

Convert Image

A primeira imagem é a original. A segunda é com o filtro de mediana aplicado. A terceira com a redução do espaço de cores. A quarta é com o “endireitamento” da imagem. E a quinta imagem é com a seleção de caracteres.

Convert Image-1

Convert Image-1

Para fontes pontilhadas o filtro de ruídos tem que ser um pouco mais suave. Ele erra bastante, mas também acerta. Cada operação dessa demora poucos milissegundos, como não há limite de tentativas e nem tempo de espera no site da Receita Federal, mesmo com vários erros a velocidade de descoberta do valor do CAPTCHA é muito baixa, poucos segundos. O mais demorado é o treinamento do OCR que é feito uma única vez. Para OCR utilizei a biblioteca JavaOCR.

Para demonstrar o “endireitamento” da imagem, que foi o que mais deu trabalho, pois foi na tentativa e erro, vou exibir uma imagem utilizando somente essa transformação, aí fica fácil entender o motivo das últimas imagens serem maiores. Eu poderia ter identificado pontos da imagem e feito interpolação de pontos para traçar uma curva, mas como é só pra conceito, fiz da forma que achei mais simples.

Endireitamento

Endireitamento

Criei um filtro de “efeito bandeira” :). Por isso que a imagem fica maior. Portanto, se você quer um sistema seguro, a primeira dica é, coloque um limite máximo de tentativas, obrigue uma espera de alguns minutos para a próxima tentativa e coloque algumas perguntas inteligentes além do CAPTCHA.

Sobre: Thiago Galbiatti Vespa

Thiago Galbiatti Vespa é mestre em Ciências da Computação e Matemática Computacional pela USP e bacharel em Ciências da Computação pela UNESP. Coordenador de projetos do JavaNoroeste, membro do JCP (Java Community Process), consultor Oracle, arquiteto de software de empresas de médio e grande porte, palestrante de vários eventos e colaborador de projetos open source. Possui as certificações: Oracle Certified Master, Java EE 5 Enterprise Architect – Step 1, 2 and 3; Oracle WebCenter Portal 11g Certified Implementation Specialist; Oracle Service Oriented Architecture Infrastructure Implementation Certified Expert; Oracle Certified Professional, Java EE 5 Web Services Developer; Oracle Certified Expert, NetBeans Integrated Development Environment 6.1 Programmer; Oracle Certified Professional, Java Programmer; Oracle Certified Associate, Java SE 5/SE 6