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:
-
/**
-
* This method should handle any authentication and report back to the subject
-
*
-
* @access public
-
* @param array $credentials Array holding the user credentials
-
* @param array $options Array of extra options
-
* @param object $response Authentication response object
-
* @return boolean
-
* @since 1.5
-
*/
-
function onAuthenticate( $credentials, $options, &$response )
-
{
-
jimport('joomla.user.helper');
-
-
// Joomla does not like blank passwords
-
{
-
$response->status = JAUTHENTICATE_STATUS_FAILURE;
-
$response->error_message = 'Empty password not allowed';
-
return false;
-
}
-
-
// Initialize variables
-
$conditions = '';
-
-
// Get a database object
-
$db =& JFactory::getDBO();
-
-
$query = 'SELECT `id`, `password`, `gid`'
-
. ' FROM `#__users`'
-
. ' WHERE username=' . $db->Quote( $credentials['username'] )
-
;
-
$db->setQuery( $query );
-
$result = $db->loadObject();
-
-
if($result)
-
{
-
$crypt = $parts[0];
-
$salt = @$parts[1];
-
$testcrypt = JUserHelper::getCryptedPassword($credentials['password'], $salt);
-
-
if ($crypt == $testcrypt) {
-
$user = JUser::getInstance($result->id); // Bring this in line with the rest of the system
-
$response->email = $user->email;
-
$response->fullname = $user->name;
-
$response->status = JAUTHENTICATE_STATUS_SUCCESS;
-
$response->error_message = '';
-
} else {
-
$response->status = JAUTHENTICATE_STATUS_FAILURE;
-
$response->error_message = 'Invalid password';
-
}
-
}
-
else
-
{
-
$response->status = JAUTHENTICATE_STATUS_FAILURE;
-
$response->error_message = 'User does not exist';
-
}
-
Â
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:
- /**
- * Formats a password using the current encryption.
- *
- * @access public
- * @param string $plaintext The plaintext password to encrypt.
- * @param string $salt The salt to use to encrypt the password. []
- * If not present, a new salt will be
- * generated.
- * @param string $encryption The kind of pasword encryption to use.
- * Defaults to md5-hex.
- * @param boolean $show_encrypt Some password systems prepend the kind of
- * encryption to the crypted password ({SHA},
- * etc). Defaults to false.
- *
- * @return string The encrypted password.
- */
- function getCryptedPassword($plaintext, $salt = '', $encryption = 'md5-hex', $show_encrypt = false)
- {
- // Get the salt to use.
- $salt = JUserHelper::getSalt($encryption, $salt, $plaintext);
- Â
- // Encrypt the password.
- switch ($encryption)
- {
- case 'plain' :
- return $plaintext;
- Â
- case 'sha' :
- return ($show_encrypt) ? '{SHA}'.$encrypted : $encrypted;
- Â
- case 'crypt' :
- case 'crypt-des' :
- case 'crypt-md5' :
- case 'crypt-blowfish' :
- Â
- case 'md5-base64' :
- return ($show_encrypt) ? '{MD5}'.$encrypted : $encrypted;
- Â
- case 'ssha' :
- return ($show_encrypt) ? '{SSHA}'.$encrypted : $encrypted;
- Â
- case 'smd5' :
- return ($show_encrypt) ? '{SMD5}'.$encrypted : $encrypted;
- Â
- case 'aprmd5' :
- $context = $plaintext.'$apr1$'.$salt;
- Â
- for ($i = $length; $i > 0; $i -= 16) {
- }
- Â
- for ($i = $length; $i > 0; $i >>= 1) {
- }
- Â
- Â
- for ($i = 0; $i < 1000; $i ++) {
- if ($i % 3) {
- $new .= $salt;
- }
- if ($i % 7) {
- $new .= $plaintext;
- }
- }
- Â
- for ($i = 0; $i < 5; $i ++) {
- $k = $i +6;
- $j = $i +12;
- if ($j == 16) {
- $j = 5;
- }
- }
- Â
- Â
- case 'md5-hex' :
- default :
- return ($show_encrypt) ? '{MD5}'.$encrypted : $encrypted;
- }
- }
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ó...
| < Anterior | Próximo > |
|---|




Comentário
qual e a ideia
mostrar como o joomla e foda na segurança
ou como eu posso quebrar a segurança do joomla
mostrar como o joomla e foda na segurança
ou como eu posso quebrar a segurança do joomla
I really enjoyed the article. It proved to be Very helpful to me and I am sure to all the comment here!
Site has been added to my RSS feed for later browsing because your blog is necessary forever. To know more about.