Portal do Joomla Rio de Janeiro
 
 

Enquete

Quais as cores para o novo Portal?
 
Banner

Como as senhas são criptografadas no Joomla

Avaliação do Usuário: / 8
PiorMelhor 

Antes de sujar a mão de graxa

Neste tutorial estou falando do método de encriptação de senhas do Joomla 1.5.x. Para reforçar a segurança deste CMS, estas versões apresentam um método bem mais robusto quando comparado com o método oferecido pelas versões mais antigas 1.0.x. Se você ainda não atualizou para a 1.5.x, está mais do que na hora de pensar em fazer isto - não só pelo aspecto segurança, mas também porque as versões 1.0.x vão ser descontinuadas.

Onde estão guardadas as informações

Não fui eu quem inventou o sistema de criptografia de senhas do Joomla, mas, como a criptografia é um dos meus assuntos preferidos, fui dar uma espiada no que a turma dos joomleiros fez. Localizei dois arquivos essenciais para entender a mecânica da cifragem: o primeiro é o arquivo joomla.php, que faz parte do plugin de autenticação; o segundo é o helper.php, que faz parte do conjunto de arquivos user da biblioteca joomla. Estão nos seguintes diretórios:

* /plugins/authentication/joomla.php
* /libraries/joomla/user/helper.php

Inicialmente abra o joomla.php e dê uma olhada no método onAutheticate:

  1. /**
  2. * This method should handle any authentication and report back to the subject
  3. *
  4. * @access public
  5. * @param array $credentials Array holding the user credentials
  6. * @param array $options Array of extra options
  7. * @param object $response Authentication response object
  8. * @return boolean
  9. * @since 1.5
  10. */
  11. function onAuthenticate( $credentials, $options, &$response )
  12. {
  13. jimport('joomla.user.helper');

  14. // Joomla does not like blank passwords
  15. if (empty($credentials['password']))
  16. {
  17. $response->status = JAUTHENTICATE_STATUS_FAILURE;
  18. $response->error_message = 'Empty password not allowed';
  19. return false;
  20. }

  21. // Initialize variables
  22. $conditions = '';

  23. // Get a database object
  24. $db =& JFactory::getDBO();

  25. $query = 'SELECT `id`, `password`, `gid`'
  26. . ' FROM `#__users`'
  27. . ' WHERE username=' . $db->Quote( $credentials['username'] )
  28. ;
  29. $db->setQuery( $query );
  30. $result = $db->loadObject();

  31. if($result)
  32. {
  33. $parts = explode( ':', $result->password );
  34. $crypt = $parts[0];
  35. $salt = @$parts[1];
  36. $testcrypt = JUserHelper::getCryptedPassword($credentials['password'], $salt);

  37. if ($crypt == $testcrypt) {
  38. $user = JUser::getInstance($result->id); // Bring this in line with the rest of the system
  39. $response->email = $user->email;
  40. $response->fullname = $user->name;
  41. $response->status = JAUTHENTICATE_STATUS_SUCCESS;
  42. $response->error_message = '';
  43. } else {
  44. $response->status = JAUTHENTICATE_STATUS_FAILURE;
  45. $response->error_message = 'Invalid password';
  46. }
  47. }
  48. else
  49. {
  50. $response->status = JAUTHENTICATE_STATUS_FAILURE;
  51. $response->error_message = 'User does not exist';
  52. }
  53.  



Não é preciso destrinchar este código todinho (o que seria uma chatice), vou apenas apontar as partes que nos interessam. As linhas 70 a 78 mostram que o plugin acessou o banco de dados do Joomla para buscar algumas informações na tabela de usuários filtrando os dados pelo nome de usuário. Depois disto, se tiver encontrado alguma coisa, explode o que encontrou no campo password usando o delimitador ":" (linha 82). A primeira parte (a senha criptografada) é armazenada na variável $crypt; a segunda (o chamado sal) é colocada na variável $salt. Isto indica que as senhas guardadas são compostas por duas partes. Alguns exemplos são:


3291503d0bd0059c54b466049594c443:Tk7F78S1VpAAGgPB
69a02eba32f545982806168859ebb08b:kQFgQ54RngbuYhsC

Logo a seguir, o método getCryptedPassword, que pertence à classe JUserHelper, é chamado com os parâmetros senha e sal (veja na linha 85). O que faz e onde está esta função? Na biblioteca do Joomla, mais especificamente no segundo arquivo que nos interessa: /libraries/joomla/user/helper.php. Veja a seguir o que são estas duas partes e como devem ser tratadas para que possamos conferir se determinado usuário está fornecendo a senha correta.

A criptografia das senhas do Joomla

No Joomla 1.0.x as senhas são transformadas num hash MD5. Se você não sabe o que é um hash, sugiro que dê uma lida nos artigos que publiquei em Criptografia/Funções Hash. Na versão atual, este hash foi incrementado (entenda-se, a segurança foi reforçada) adicionando-se um pouco de tempero, o chamado salt. A coisa funciona mais ou menos assim: "encomprida-se" a senha com mais uma porção de bits e só depois se aplica o hash MD5. Acontece que, se não soubermos qual salt foi usado para temperar a senha, não tem como repetir o processo para poder comparar a senha recém-fornecida com a senha que está no banco de dados. Daí separar a senha que foi "hasheada" junto com o sal do sal propriamente dito com o delimitador ":". Se tivermos os dois, o novo "hasheamento" pode ser comparado com o armazenado

Quem pode nos contar como todo este processo é realizado é o método getCryptedPassword da classe JUserHelper:


  1. /**
  2. * Formats a password using the current encryption.
  3. *
  4. * @access public
  5. * @param string $plaintext The plaintext password to encrypt.
  6. * @param string $salt The salt to use to encrypt the password. []
  7. * If not present, a new salt will be
  8. * generated.
  9. * @param string $encryption The kind of pasword encryption to use.
  10. * Defaults to md5-hex.
  11. * @param boolean $show_encrypt Some password systems prepend the kind of
  12. * encryption to the crypted password ({SHA},
  13. * etc). Defaults to false.
  14. *
  15. * @return string The encrypted password.
  16. */
  17. function getCryptedPassword($plaintext, $salt = '', $encryption = 'md5-hex', $show_encrypt = false)
  18. {
  19. // Get the salt to use.
  20. $salt = JUserHelper::getSalt($encryption, $salt, $plaintext);
  21.  
  22. // Encrypt the password.
  23. switch ($encryption)
  24. {
  25. case 'plain' :
  26. return $plaintext;
  27.  
  28. case 'sha' :
  29. $encrypted = base64_encode(mhash(MHASH_SHA1, $plaintext));
  30. return ($show_encrypt) ? '{SHA}'.$encrypted : $encrypted;
  31.  
  32. case 'crypt' :
  33. case 'crypt-des' :
  34. case 'crypt-md5' :
  35. case 'crypt-blowfish' :
  36. return ($show_encrypt ? '{crypt}' : '').crypt($plaintext, $salt);
  37.  
  38. case 'md5-base64' :
  39. $encrypted = base64_encode(mhash(MHASH_MD5, $plaintext));
  40. return ($show_encrypt) ? '{MD5}'.$encrypted : $encrypted;
  41.  
  42. case 'ssha' :
  43. $encrypted = base64_encode(mhash(MHASH_SHA1, $plaintext.$salt).$salt);
  44. return ($show_encrypt) ? '{SSHA}'.$encrypted : $encrypted;
  45.  
  46. case 'smd5' :
  47. $encrypted = base64_encode(mhash(MHASH_MD5, $plaintext.$salt).$salt);
  48. return ($show_encrypt) ? '{SMD5}'.$encrypted : $encrypted;
  49.  
  50. case 'aprmd5' :
  51. $length = strlen($plaintext);
  52. $context = $plaintext.'$apr1$'.$salt;
  53. $binary = JUserHelper::_bin(md5($plaintext.$salt.$plaintext));
  54.  
  55. for ($i = $length; $i > 0; $i -= 16) {
  56. $context .= substr($binary, 0, ($i > 16 ? 16 : $i));
  57. }
  58.  
  59. for ($i = $length; $i > 0; $i >>= 1) {
  60. $context .= ($i & 1) ? chr(0) : $plaintext[0];
  61. }
  62.  
  63. $binary = JUserHelper::_bin(md5($context));
  64.  
  65. for ($i = 0; $i < 1000; $i ++) {
  66. $new = ($i & 1) ? $plaintext : substr($binary, 0, 16);
  67. if ($i % 3) {
  68. $new .= $salt;
  69. }
  70. if ($i % 7) {
  71. $new .= $plaintext;
  72. }
  73. $new .= ($i & 1) ? substr($binary, 0, 16) : $plaintext;
  74. $binary = JUserHelper::_bin(md5($new));
  75. }
  76.  
  77. $p = array ();
  78. for ($i = 0; $i < 5; $i ++) {
  79. $k = $i +6;
  80. $j = $i +12;
  81. if ($j == 16) {
  82. $j = 5;
  83. }
  84. $p[] = JUserHelper::_toAPRMD5((ord($binary[$i]) << 16) | (ord($binary[$k]) << 8) | (ord($binary[$j])), 5);
  85. }
  86.  
  87. return '$apr1$'.$salt.'$'.implode('', $p).JUserHelper::_toAPRMD5(ord($binary[11]), 3);
  88.  
  89. case 'md5-hex' :
  90. default :
  91. $encrypted = ($salt) ? md5($plaintext.$salt) : md5($plaintext);
  92. return ($show_encrypt) ? '{MD5}'.$encrypted : $encrypted;
  93. }
  94. }

A função getCryptedPassword (linha 106) precisa no mínimo de um parâmetro: a senha em texto claro ($plaintext); os outros três parâmetros são opcionais. Se o sal ($salt) não for fornecido, ele recebe uma string vazia; se o tipo de encriptação ($encryption) não for definido, o valor padrão 'md5-hex' é assumido e se mostrar o tipo de encriptação ($show_encrypt) não for especificado, o padrão será falso. Em princípio, se chamarmos a função apenas com a senha fornecida pelo usuário, o sal não será usado, o tipo de cifragem será md5-hex e o tipo de encriptação não será adicionado ao resultado. É a cifragem mais simples que existe.

As linhas restantes desta função tratam de identificar o tipo de encriptação solicitado e realizam a cifragem de acordo. Para nós interessam apenas as linhas finais, onde o hash do tipo md5-hex é calculado. Se o sal estiver presente, a variável $encrypted recebe o valor de retorno da função do PHP md5() aplicada ao conjunto $plaintext.$salt; se não estiver presente, a mesma função do PHP é aplicada apenas a $plaintext. Da mesma forma, se $show_encrypt for verdadeiro, o tipo de cifragem é adicionado ao resultado pela concatenação de '{MD5}'.$encrypted; caso contrário, o valor de retorno será apenas o hash obtido.

Usando o banco de dados do Joomla fora do contexto Joomla

<?php
// Fazer conexão com o banco de dados
$db = mysql_connect("localhost", "usuarioBD", "senhaBD");
mysql_select_db("baseDeDados", $db);

... // Aqui as senhas serão comparadas

mysql_close($db);
?>

Precisamos encontrar a senha cifrada do usuário indicado no formulário:

<?php
// Pegar nome de usuário e senha do formulário
$usuario = $_POST['usuario'];
$senha = $_POST['senha'];

// Fazer conexão com o banco de dados
$db = mysql_connect("localhost", "usuarioBD", "senhaBD");
mysql_select_db("baseDeDados", $db);

// Obter senha cifrada do usuário
$sql = "SELECT password FROM jos_users WHERE username='$usuario'";
$resultado = mysql_query($sql);

// Não precisamos mais da conexão, podemos fechar o banco de dados
mysql_close($db);
?>

Tendo a senha criptografada, agora é preciso cifrar a senha fornecida pelo formulário e comparar as duas. Se a senha foi criptografada com um salt, sabemos que ela é composta por duas partes separadas pelo delimitador ":"; se não foi usado um salt, o delimitador não existe. Aqui podemos pegar uma carona no código do plugin para obter as partes da senha armazenada no banco de dados e depois completar o script com uma pequena rotina que cifra a senha fornecida pelo usuário e compara o resultado com a senha do banco de dados:

<?php
// Pegar nome de usuário e senha do formulário
$usuario = $_POST['usuario'];
$senha = $_POST['senha'];

// Fazer conexão com o banco de dados
$db = mysql_connect("localhost", "usuarioBD", "senhaBD");
mysql_select_db("baseDeDados", $db);

// Obter senha cifrada do usuário
$sql = "SELECT password FROM jos_users WHERE username='$usuario'";
$resultado = mysql_query($sql);

// Não precisamos mais da conexão, podemos fechar o banco de dados
mysql_close($db);

// Extrair os dados do resultado da query
$pega = mysql_fetch_array($resultado);
$senhaCripto = $pega['password'];

// Separar a senha do sal
if($senhaCripto)
{
$partes = explode( ':', $senhaCripto );
$cripto = $partes[0];
$sal = $partes[1];

// Criar hash com a senha fornecida com o sal (se houver)
$novoHash = ($sal) ? md5($senha.$sal) : md5($senha);
if( $novoHash == $cripto ) {
echo "Acesso autorizado";
} else {
echo "Acesso negado. A senha não confere!";
}

} else {
echo "Este usuário não está cadastrado";
}
?>


É claro que este script pode (e deve) ser melhorado. Por exemplo, deveria haver tratamento de erros como falha na conexão, etc. Mas este é apenas um esqueleto de script que serve somente para passar a idéia de como podemos implementar um sisteminha de checagem de senhas armazenadas no banco de dados do Joomla.

Grande abraço da vó...

Comentário

avatar bene
0
 
 
oi vó
qual e a ideia
mostrar como o joomla e foda na segurança
ou como eu posso quebrar a segurança do joomla
Nome *
Email (For verification & Replies)
URL
Code   
ChronoComments by Joomla Professional Solutions
Enviar Comentário
Cancelar
avatar LarsonNatalie27
0
 
 
I propose not to wait until you get big sum of money to order goods! You should take the credit loans or consolidation loans and feel free
Nome *
Email (For verification & Replies)
URL
Code   
ChronoComments by Joomla Professional Solutions
Enviar Comentário
Cancelar
I actually was afraid using progressive research papers for the first-ever period. Butpresently I am steady customerand advise everyone use them. Custom research papers service is your privacy.
Nome *
Email (For verification & Replies)
URL
Code   
ChronoComments by Joomla Professional Solutions
Enviar Comentário
Cancelar
People have to guess two times before they begin their essay topic completing, just because this can be really easier to buy essay online from exploratory essay writing service. Furthermore, it can save time!
Nome *
Email (For verification & Replies)
URL
Code   
ChronoComments by Joomla Professional Solutions
Enviar Comentário
Cancelar
avatar papers for sale
0
 
 
We can speak a lot just about the chronicle of term paper writing, but I would tell that the write my paper service could compose the high quality essays for sale always. Is that correct?
Nome *
Email (For verification & Replies)
URL
Code   
ChronoComments by Joomla Professional Solutions
Enviar Comentário
Cancelar
avatar Seiko Kinetic
0
 
 
qual e a ideia
mostrar como o joomla e foda na segurança
ou como eu posso quebrar a segurança do joomla
Nome *
Email (For verification & Replies)
URL
Code   
ChronoComments by Joomla Professional Solutions
Enviar Comentário
Cancelar
avatar wiliam aquino
0
 
 
Cara qual motivo as URLS deste site Joomlarj não é amigavel:?
Nome *
Email (For verification & Replies)
URL
Code   
ChronoComments by Joomla Professional Solutions
Enviar Comentário
Cancelar
avatar Breville
0
 
 
As linhas restantes desta função tratam de identificar o tipo de encriptação solicitado e realizam a cifragem de acordo
Nome *
Email (For verification & Replies)
URL
Code   
ChronoComments by Joomla Professional Solutions
Enviar Comentário
Cancelar
avatar Deivison Arthur
0
 
 
Expetacular o tutorial parabéns! vai me ajudar muito futuramente
Nome *
Email (For verification & Replies)
URL
Code   
ChronoComments by Joomla Professional Solutions
Enviar Comentário
Cancelar
where can I get this code in English
Nome *
Email (For verification & Replies)
URL
Code   
ChronoComments by Joomla Professional Solutions
Enviar Comentário
Cancelar
avatar dual sim mobile
0
 
 

I really enjoyed the article. It proved to be Very helpful to me and I am sure to all the comment here!

Nome *
Email (For verification & Replies)
URL
Code   
ChronoComments by Joomla Professional Solutions
Enviar Comentário
Cancelar
avatar nike shox
0
 
 


Site has been added to my RSS feed for later browsing because your blog is necessary forever. To know more about.
Nome *
Email (For verification & Replies)
URL
Code   
ChronoComments by Joomla Professional Solutions
Enviar Comentário
Cancelar
I should try this hint too
Nome *
Email (For verification & Replies)
URL
Code   
ChronoComments by Joomla Professional Solutions
Enviar Comentário
Cancelar
Nome *
Email (For verification & Replies)
URL
Code   
ChronoComments by Joomla Professional Solutions
Enviar Comentário
 
 
Copyright © 2010 Joomla RJ - tutorial joomla - joomla modulos - joomla extensions - joomla templates. Todos os direitos reservados.
Joomla! é um Software Livre com licença GNU/GPL v2.0.