Gerando tabelas de hash MD5

A idéia veio do nada. Na verdade, eu estava fazendo um freelance e procurando uma função pra “ordenar” um vetor aleatoriamente no PHP (no fim usei usort, que recebe uma função de comparação tipo o qsort do C) e me deparei com um comentário lá no PHP.net de um cara que usou geração de números aleatórios para criar um algoritmo de criptografia pra usar no lugar de MD5 e SHA, porque ele não confiava mais no MD5 e no SHA porque segundo ele existem tabelas na internet, o que torna um sistema muito vulnerável (para quem tem acesso ao banco de dados).

Procurei na internet e na verdade eu não encontrei muitos bancos de dados com vários MD5 não, ao menos não visíveis no Google quando se procura por um hash. Só pra strings como “1234”, mas não achei nem pra “060790” (minha data de nascimento). Veja você mesmo: 81811b48cc07432fc550cb42d4ab3e8f

Então pensei: são 31 dias por mês, 12 meses por ano, 100 anos considerando que os anos são representados por dois dígitos. Isso me dá 31 x 12 x 100 = apenas 37200 hashes. O custo pra gerar isso deve ser minúsculo, por que ninguém faz?

A idéia de fazer com essas datas de nascimento veio do fato de vários usuários leigos que conheço utilizarem data de nascimento pra suas senhas (muito também pelo fato de eles usarem essa mesma senha nas senhas de seis dígitos numéricos do banco e esse tipo de coisa). Então lá fui eu pro código. A princípio escrevi em cerca de 30 segundos – 1 minuto o seguinte código em Ruby:

require "md5"

100.times do |ano|
  12.times do |j|
    mes = j+1

    31.times do |i|
      dia = i+1
      string = sprintf("%02d%02d%02d", dia, mes, ano)

      md5 = MD5.new(string).to_s
      puts "#{string}: #{md5}"
    end
  end
end

É funcional e até eficiente…

tiago@flick ~ $ time ruby genmd5.rb > hashes-ruby

real    0m1.662s
user    0m0.620s
sys     0m0.028s

Mas minha geekialidade não permitiu que eu parasse por aqui. Nesse momento eu já nem me lembrava do freelance que estava fazendo e resolvi ver como usar MD5 em C. Meu primeiro chute foi um man md5 no terminal, que já me retornou a resposta da vida, do universo e tudo mais: o cabeçalho openssl/md5.h e sua função MD5:

unsigned char *MD5(const unsigned char *d, unsigned long n,
                 unsigned char *md);

Então lá fui eu pro programa:

#include <stdio.h>
#include <openssl/md5.h>

int main() {
  int dia, mes, ano;
  unsigned char string[STRING_LENGTH], md[MD5_DIGEST_LENGTH];
  for (ano = 0; ano < 100; ano++) {
    for (mes = 0; mes < 12; mes++) {
      for (dia = 0; dia < 31; dia++) {
        sprintf(string, "%02d%02d%02d", dia+1, mes+1, ano);
        printf("%sn", MD5(string, sizeof(string), md));
      }
    }
  }
  return 0;
}

Ao rodar, recebi saídas com caracteres estranhos e nenhum resultado visível. Corri pro Google. Procurei, procurei, e NINGUÉM usa essa maldita função MD5 do C num programa simples e não há exemplos nem howto de como utilizá-la. Depois de uns 30 minutos quebrando a cabeça (pra mais), percebi que alguns códigos que usavam isso na internet usavam %x (é o código pro printf imprimir inteiros hexadecimais) para imprimir caracteres do MD5 na tela. Aí encontrei algo assim:

printf("%x%x...%x%x", md[0], md[1], ..., md[14], md[15]);

(e pior que estou falando sério… tem gente na internet que não conhece for!)

E caiu a ficha. O MD5 tem 16 inteiros hexadecimais de um byte, 32 caracteres. Escrever o código abaixo me tomou bastante tempo, mas uma aprendizagem interessante e um ânimo pra voltar pro meu freelance:

#include <stdio.h>
#include <openssl/md5.h>

#define STRING_LENGTH 6

int main() {
  int dia, ano, mes;
  int i;
  unsigned char md[MD5_DIGEST_LENGTH], string[STRING_LENGTH];

  for (ano = 0; ano < 100; ano++) {
    for (mes = 0; mes < 12; mes++) {
      for (dia = 0; dia < 31; dia++) {
        sprintf(string, "%02d%02d%02d", dia+1, mes+1, ano);
        MD5(string, sizeof(string), md);
        printf("%s: ", string);
        for (i = 0; i < 16; i++) {
          printf("%02x", md[i]); /* Note isso aqui! */
        }
        printf("n");
      }
    }
  }
  return 0;
}

O “Note isso aqui!” ainda foi uma sacanagem incrível, o código que eu disse do cara que imprimia %x%x%x… não funciona na prática, porque quando um dos dígitos é 0X, ele só imprime X.

Enfim compilei meu código com -lssl (importante para C-zeiros de primeira viagem) e voi lá:

tiago@flick ~ $ time ./md5 > hashes-c

real    0m0.397s
user    0m0.136s
sys     0m0.028s

Confesso que é coisa de nerd mesmo querer otimizar um código que só serve pra gerar um arquivo de hashes e já o fez, mas no momento em que terminei esse teste, pensei: preciso postar isso no blog pra documentar o uso da função MD5 no C, antes que mais alguém perca o tempo que eu perdi.

Pra quem se pergunta se eu realmente fiz tudo certo:

tiago@flick ~ $ diff senhas-c senhas-ruby
tiago@flick ~ $

(i.e. ou eu errei nos dois ou eu não errei em nenhum)

Útil, não? Não. Na verdade isso não serve pra absolutamente nada, a não ser que você roube o banco de dados de alguém e esse alguém usa como senha a data de nascimento da sua mãe. Mas aí ele merece mesmo que você pegue sua senha, então isso não muda nada.

Dá pra adaptar o código pra gerar outras tabelas, mas por favor não use isso pra nada maligno, e falo sério. Meus fins foram absolutamente educacionais/acadêmicos (eu queria aprender a trabalhar com a função MD5 da biblioteca OpenSSL, não dominar o mundo) e estou postando aqui para ajudar os que também vão querer usar essa função.

Comentários de crackers serão ignorados.

Publicidade gratuita

Alguém quase sem nada pra fazer resolveu mandar o Lynx baixar todas as médias do ENEM, rodou um programa em PHP com expressões regulares para separar os valores e agora apresenta-lhes a escola com a melhor média de Santa Catarina:

mysql> select id, nome, media from enem where estado='sc' order by media desc limit 40;
+-----+-------------------------------------------+-------+
| id  | nome                                      | media |
+-----+-------------------------------------------+-------+
| 185 | COLEGIO SALESIANO ITAJAI                  |  6456 |
| 821 | INST SUP E CENTRO EDUC LUTERANO BOM JESUS |  6435 |
| 140 | COLEGIO ENERGIA                           |  6295 |
|  49 | CENTRO EDUC ENERGIA TUBARAO SC LTDA       |  6209 |
|  87 | COLEGIO BOM JESUS DIOCESANO               |  6167 |
| 154 | COLEGIO HENRY FORD                        |  6144 |
| 193 | COLEGIO SAO BENTO                         |  6144 |
| 834 | SOCIEDADE EDUC POSIVILLE LTDA             |  6085 |
| 166 | COLEGIO MARISTA SAO LUIS                  |  6073 |
| 142 | COLEGIO ENERGIA                           |  6060 |
| 813 | EXATHUM CURSO E COLEGIO                   |  6034 |
| 116 | COLEGIO CORACAO DE JESUS                  |  5984 |
|  48 | CENTRO EDUC ENERGIA SC LTDA               |  5959 |
|  68 | CENTRO FEDERAL DE ED TECNOLOGICA DE SC    |  5936 |
| 173 | COLEGIO MURIALDO                          |  5909 |
| 115 | COLEGIO CONSUL CARLOS RENAUX              |  5899 |
| 206 | COLEGIO TENDENCIA                         |  5885 |
| 128 | COLEGIO DEHON                             |  5872 |
|  76 | COLEGIO ALTO VALE LTDA                    |  5861 |
|  88 | COLEGIO BOM JESUS STO ANTONIO             |  5852 |
| 365 | EEB FELICIANO NUNES PIRES                 |  5843 |
| 119 | COLEGIO DE APLICACAO DA UNC CACADOR       |  5831 |
| 150 | COLEGIO EXPONENCIAL                       |  5811 |
| 182 | COLEGIO RAINHA DO MUNDO                   |  5796 |
| 183 | COLEGIO SAGRADA FAMILIA                   |  5788 |
| 160 | COLEGIO MAFRENSE                          |  5779 |
|  96 | COLEGIO CENECISTA DR JULIO CESAR R NEVES  |  5777 |
| 210 | CONJ EDUC DR BLUMENAU                     |  5761 |
|  51 | CENTRO EDUC FRAIBURGO CEFRAI              |  5746 |
| 184 | COLEGIO SAGRADA FAMILIA                   |  5746 |
| 164 | COLEGIO MARISTA FREI ROGERIO              |  5724 |
|  69 | COLEGIO ADV DE  INDAIAL                   |  5721 |
| 196 | COLEGIO SAO LUIZ                          |  5720 |
| 159 | COLEGIO MADRE TERESA MICHEL               |  5719 |
| 801 | ESC BARAO DO RIO BRANCO                   |  5717 |
| 835 | SOCIEDADE EDUC VERDES MARES               |  5696 |
| 211 | COOPERATIVA EDUCACIONAL MAGNA             |  5688 |
| 133 | COLEGIO DOM JAIME CAMARA                  |  5683 |
| 139 | COLEGIO ELISA ANDREOLI                    |  5681 |
| 134 | COLEGIO DOS SANTOS ANJOS                  |  5673 |
+-----+-------------------------------------------+-------+
40 rows in set (0,00 sec)

A melhor escola possui também o melhor site, que deve ter sido feito por alguém realmente muito bom. ;)

Feeds RSS para fotologs

Não tenho programado absolutamente nada, a não ser nos trabalhos remunerados. Eu vejo a programação como uma arte… Programar é como compôr uma música, pintar, atuar; é preciso haver inspiração. Eu não ando inspirado para programar, mas nesse caso a necessidade me levou a criação de um script que pode ter utilidade para algum leitor perdido…

No ano passado, eu escrevi um script que gerava feeds RSS para fotologs do fotolog.net. O tempo passou, o meu usuário na UNICAMP foi deletado e nessa semana eu voltei a ter a necessidade de acompanhar um fotolog. Então, resolvi reescrever o script, agora com mais funcionalidades:

  • Compatível com Bloglines
  • Tem como descrição o “título” que o usuário coloca no Fotolog.net.
  • É atualizado sempre que há novos comentários nos últimos 6 posts.
  • Você pode comentar de dentro do seu leitor de feeds (ele copia o formulário de comentário pro feed).
  • Ele faz cache de tudo.

Resolvi presentear todo mundo usando o presente que a Dreamhost me deu no mês passado: 200gb de espaço e 2tb de banda tinham que servir pra alguma coisa no meu servidor. Então, ao invés de disponibilizar o código, eu criei uma página PHP que gera o feed que você quiser para você:

http://fotolog.tiagomadeira.net/USUARIO

Para ver um feed em ação no Bloglines, entre no meu diretório de feeds e procure um feed com prefixo “fotolog.com/”.

Depois de acabar de criar o script, ainda lembrei que o IE7 foi lançado nessa semana com suporte a feeds! Então, esse negócio pode ser útil pra bastante gente.

Acompanhe os fotologs dos seus amigos no Bloglines, divulgue aos seus amigos, comente sugerindo melhorias; aproveite enquanto eu ainda estou inspirado pra corrigir os defeitos! =)

LiveSearch no WordPress

O Cosme me apresentou hoje um projeto chamado LiveSearch. É uma aplicação Ajax bem simples (eu mesmo conseguiria fazer), mas uma idéia que eu ainda não havia tido.

Resolvi então implementar o negócio no meu blog e estou escrevendo esse artigo pra explicar como instalamos o LiveSearch de forma que ele funcione com o WordPress e dentro dos padrões de XHTML Strict (ele pede originalmente que utilizamos name num form e exige que o nome de um campo seja “q” ao invés do “s” do WordPress).

Então, mãos a obra!

1. livesearch.php

Para começar, você deve criar um arquivo e chamá-lo livesearch.php. O seu conteúdo deve ser:

<?php if (have_posts()) : ?>


<ul class="LSRes">
  <?php while (have_posts()) : the_post(); ?>


  <li class="LSRow">
    <a href="<?php the_permalink(); ?>"><?php the_title(); ?></a>

  </li>


  <?php endwhile; ?>

</ul>


<?php endif; ?>

Para facilitar para quem quer aprender PHP, vou explicar linha por linha:

  1. Se houver posts, faça:
  2. Lista HTML
  3. Enquanto houver posts: Execute a função the_post().
  4. Item da Lista HTML
  5. Linca para o permalink do post com o texto do link = seu título.
  6. Fim do “Item da Lista HTML”
  7. Fim do “Enquanto houver posts”
  8. Fim da “Lista HTML”
  9. Fim do “Se houver posts”

Observação: As cores são só para reforçar a hierarquia.

O que este arquivo faz, portanto, é criar uma lista com o título dos posts. Essas funções são todas do WordPress (haveposts, thetitle, the_permalink, etc.). Se você quisesse colocar outras coisas na lista que vai aparecer no resultado, pode colocar aí nesse arquivo.

No fim, salve esse arquivo em /wp-content/themes/SEU_TEMA/livesearch.php.

2. Redirecionar para livesearch.php se for uma pesquisa “live”

Coloque no início do arquivo /wp-content/themes/SEU_TEMA/search.php o seguinte conteúdo:

<?php if ($_GET["live"]) {
		include("livesearch.php");
		die();
	} ?>
  1. Se a query string live “existir”, faça:
  2. Inclua o arqiuvo livesearch.php nessa página.
  3. Morra!!! :D Isso significa para ele parar de executar qualquer coisa que esteja debaixo disso.
  4. Fim do “Se”

Isso serve para que possamos fazer as buscas de duas maneiras. Uma é a busca normal, que é acessada por /?s=queries e outra é a live que será acessada por /?live=1&s=queries (ou seja, a mesma coisa mas dando valor à variável “live”).

3. JavaScripts

Baixe o arquivo livesearch.js e coloque-o na pasta /wp-content/themes/SEU_TEMA/.

Agora faremos uma série de mudanças pra manter o negócio nos padrões web… Substitua as seguintes linhas pelo texto que aparece depois do seu número:

  • 122: if (liveSearchLast != document.getElementBy(‘livesearch’).value) {
  • 126: if ( document.getElementById(‘livesearch’).value == “”) {
  • 136: liveSearchReq.open(“GET”, liveSearchRoot + “/?live=1&s=” + document.getElementById(‘livesearch’).value + liveSearchParams2);
  • 137: livesearchLast = document.getElementById(“livesearch”).value;

Isso serve para ele não depender do nome do formulário e da query, mas somente do ID do campo do formulário e para ele abrir no xmlHttpRequest /?live=1&s=QUERY ao invés do padrão /livesearch.php?q=QUERY.

4. Eventos JavaScript nos documentos

No seu arquivo /wp-content/themes/SEU_TEMA/header.php, adicione o evento onload à tag BODY:

<body onload="liveSearchInit()">

No mesmo arquivo, dentro da tag HEAD linke para o script:

<script type="text/javascript" src="/wp-content/themes/SEUTEMA/livesearch.js"></script>

Agora, no arquivo /wp-content/themes/SEU_TEMA/searchform.php, adicione um evento onsubmit ao FORM

<form onsubmit="return liveSearchSubmit()" ...

E, no mesmo arquivo, adicione um evento onKeyPress ao INPUT e também um id:

<input id="livesearch" onKeyPress="liveSearchStart();"

E ao final desse arquivo, coloque:

<div id="LSResult" style="display: none;">
<div id="LSShadow">
</div>
</div>

Se você fez tudo certo e eu não errei aqui no tutorial, o seu script deve sair funcionando! Agora personalize usando estilos pra fazer um negócio bonito! (algo que eu não fiz porque tô pra trocar totalmente o design do meu site)


A Lógica do Negócio

O arquivo search.php recebe todas as buscas. O que eu fiz criando o livesearch.php foi formatar o resultado da busca de forma diferente para ele aparecer ali debaixo do formulário de busca.

O script é bem simples. Um Ajax que simplesmente faz um xmlHttpRequest a cada keypress do cara no campo. Aliás, acho que dá pra fazer um script bem menor pra só fazer isso.

Deixei o meu site meio feio com isso aí (uma lista de bullets normais e tal…), mas é porque eu tô pra trocar o design em breve e aí vou arrumar isso tudo! (inclusive, devo colocar mais Ajax!)

Qualquer dúvida, comente ou me mande um e-mail.

© 2005–2020 Tiago Madeira