Para melhor visualização, recomendo resolução de no mínimo 1280 x 800 e navegador Mozilla Firefox


domingo, 4 de novembro de 2007

Utilizando MD5 no Oracle para geração de senhas

Por Eduardo Legatti

Olá,

Como muito de vocês já sabem, e de acordo com a nossa enciclopédia online, o MD5 (Message-Digest algorithm 5) é um algoritmo de hash de 128 bits unidirecional desenvolvido pela RSA Data Security, Inc., descrito na RFC 1321, e muito utilizado por softwares com protocolo ponto-a-ponto (P2P, ou Peer-to-Peer, em inglês), verificação de integridade e logins.

Foi desenvolvido em 1991 por Ronald Rivest para suceder ao MD4 que tinha alguns problemas de segurança. Por ser um algoritmo unidirecional, uma hash md5 não pode ser transformada novamente no texto que lhe deu origem. O método de verificação é, então, feito pela comparação das duas hash (uma da base de dados, e a outra da tentativa de login). O MD5 também é usado pera verificar a integridade de um ficheiro através, por exemplo, do programa md5sum, que cria a hash de um ficheiro. Isto pode-se tornar muito útil para downloads de ficheiros grandes, para programas P2P que constroem o ficheiro através de pedaços e estão sujeitos à corrupção dos mesmos. Como autenticação de login é utilizada em vários sistemas operacionais unix e em muitos sites com autenticação.

Antigamente, era muito comum ver desenvolvedores de sistemas se perguntando qual a melhor maneira de criptografar uma senha de login de usuário de sistema e armazená-la no banco de dados. Eu mesmo já vi vários algoritmos que realizam criptografia utilizando operadores OR, XOR e que fazem contas matemáticas utilizando a tabela ASCII junto com as funções CHR(), REVERSE() e ASCII() para que a senha de um usuário seja criptografada e posteriormente caso necessário descriptografada. No meu ponto de vista, uma senha de login de usuário que já foi criptografada, não deve ter um procedimento de descriptografia. Digo isso, porque uma senha de acesso só deve ser de conhecimento de seu dono, portanto, ter um procedimento que gera o valor original de uma string criptografada poderá ocasionar sérios problemas de segurança caso o detentor ou conhecedor desta rotina possa estar com más intenções.

Na verdade, o algoritmo MD5 não é exatamente um algoritmo de criptografia, mas pode ser usado como tal exatamente porque ele é unidirecional e como dito acima o conteúdo da string não pode ser transformada novamente no texto que lhe deu origem.


Como podemos tirar proveito do MD5 no Oracle?


Na verdade, a senha do usuário não será armazenada exatamente como uma string, mas sim como um MD5 hash gerado a partir da verdadeira senha. No exemplo abaixo a ser mostrado, a função MD5 receberá a senha e a retornará na forma de um número hexadecimal com 32 caracteres. Como não existe uma chave de decodificação, então, não será possível pegar este valor de hash e transformá-lo de volta em string.

Bom, farei uso do pacote DBMS_OBFUSCATION_TOOLKIT, presente desde a versão do Oracle 8i, para demonstrar como gerar um um valor hash.

SQL> set serveroutput on

SQL> declare
  2 v_input varchar2(2000) := 'minhasenha';
  3 hexkey varchar2(32) := null;
  4 begin
  5 hexkey := rawtohex(dbms_obfuscation_toolkit.md5(input => utl_raw.cast_to_raw(v_input)));
  6 dbms_output.put_line('O valor hash para "' || v_input || '" é "' || nvl(hexkey, '')||'".');
  7 end;
  8 /
O valor hash para "minhasenha" é "7C67E713A4B4139702DE1A4FAC672344".

Procedimento PL/SQL concluído com sucesso.




Agora irei criar uma função na qual será preciso passar como parâmetro apenas a string que se deseja que seja criado o valor hash:

SQL> create or replace function md5 (valor varchar) return varchar2 is
  2 v_input varchar2(2000) := valor;
  3 hexkey varchar2(32) := null;
  4 begin
  5 hexkey := rawtohex(dbms_obfuscation_toolkit.md5(input => utl_raw.cast_to_raw(v_input)));
  6 return nvl(hexkey,'');
  7 end;
  8 /

Função criada.

SQL> select md5('minhasenha') from dual;

MD5('MINHASENHA')
--------------------------------
7C67E713A4B4139702DE1A4FAC672344

SQL> select 'TRUE' from dual
  2  where '7C67E713A4B4139702DE1A4FAC672344' = md5('minhasenha_errada'); 

não há linhas selecionadas

SQL> select 'TRUE' from dual
  2  where '7C67E713A4B4139702DE1A4FAC672344' = md5('minhasenha');

'TRU
----
TRUE




Pronto. Como demonstrado acima, agora é só utilizar a função MD5 criada acima para gerar o valor hash e armazenar este valor na tabela e coluna apropriados. Caso queira validar o hash gerado, acesse este link e teste o valor como mostrado na figura abaixo:




Para maiores informações sobre algoritmos de criptografia como DES, 3DES, AES, RC4, 3DES_2KEY e uso dos procedimentos disponíveis nos pacotes DBMS_OBFUSCATION_TOOLKIT e também no pacote DBMS_CRYPTO (disponível no 10g), acesse a documentação no site oficial da Oracle.



12 comentários:

Luthor disse...

Muito bom o artigo, parabéns

Carlos Wherbet disse...

Ótimo, vou aprender muito com esse BLOG!!

Carlos Pacheco disse...

Excelente! Mas o que eu precisava mesmo é do código para SHA-1 com encoding em base-64.
Acham que me podem ajudar?

Eduardo Legatti disse...

Olá Carlos,

Você pode pesquisar por DBMS_CRYPTO.HASH_SH1. Tem o exemplo abaixo que talvez possa te ajudar.

DECLARE
input_string VARCHAR2(160) := 'STRING QUE DEVE SER CONVERTIDA';
raw_input RAW(128) := UTL_RAW.CAST_TO_RAW(CONVERT(input_string,'AL32UTF8','WE8ISO8859P1'));
encrypted_raw RAW(2048);
BEGIN
DBMS_OUTPUT.put_line('> ========= BEGIN TEST Hash =========');
encrypted_raw := dbms_crypto.Hash( src => raw_input,
typ => DBMS_CRYPTO.HASH_SH1);
DBMS_OUTPUT.put_line('> Hash value of input string DBMS_CRYPTO.HASH_SH1 : ' || LOWER(encrypted_raw) );

END;


Abraços

Legatti

Carlos Pacheco disse...

Viva, Legatti,
Obrigado pela rápida resposta!
Esqueci-me de mencionar que a minhya bd é uma 9i que ainda usa DBMS_CRYPTO_TOOLKIT. E não sei como passar certos parametros que aqui a nossa Autoridade Tributária exige para assinar documentos como facturas, etc.

A minha empresa passou a ter necessidade de certificar o software desenvolvido internamente de facturação.
Já criei o par de chaves pública e privada. Falta o mais difícil!
Assim a AT exemplifica como o deveremos fazer sugerindo o Openssl conforme segue com o seguinte exemplo:

O texto a assinar será:
2010-05-18;2010-05-18T11:22:19;FAC 001/14;3.12;
1.º Passo:
Guardar a mensagem a assinar
2010-05-18;2010-05-18T11:22:19;FAC 001/14;3.12;
Num ficheiro de texto (que neste exemplo designaremos Registo1.txt), certificando-se que no fim da mensagem não fica qualquer quebra de linha, apenas o ";" sem aspas.
2.º Passo:
Assinar a mensagem contida no ficheiro Registo1.txt com o seguinte comando:
openssl dgst -sha1 -sign ChavePrivada.pem -out Registo1.sha1 Registo1.txt
O ficheiro Registo1.sha1 conterá o hash em binário gerado pela aplicação OpenSSL.
3.º Passo:
Seguidamente é necessário efetuar o encoding para base 64 do ficheiro Registo1.sha1:
openssl enc -base64 -in Registo1.sha1 -out Registo1.b64 -A
O ficheiro designado por Registo1.b64 é que contém os 172 carateres em ASCII da assinatura que deverão ser transportados para a base de dados e mais tarde exportados para o campo (Hash) do SAF-T(PT).
O parâmetro -A serve apenas para a aplicação OpenSSL gerar a assinatura numa única linha evitando as quebras de linha adicionais.
Como resultado o ficheiro Registo1.b64 conterá a seguinte assinatura:
oso2FoOw4V941CwKTrv6xwzUrOtxBWCwU0yLVAqKwf0CNKZHM
ETG1XZZC4spRSyby1uDXBggplogrl8gHnvevA00UEoAvGJo9Fa3DO
A0MhZNDa9/rNvu71pp+0zHmN2ra5IWpiHcgmUYxm5qamLBk49rk
gvl7h1myKCYBKqgu60=

...
Tem ideia como consigo fazer is em pl/sql no oracle 9i ?

Um abraço
Carlos Pacheco

Eduardo Legatti disse...

Olá Carlos,

O exemplo que eu passei está utilizando a package DBMS_CRYPTO. No mais, você pode fazer uma pesquisa no Google buscando sobre "Implementing the SHA1 Algorithm DBMS_CRYPTO" que você achará alguma coisa.

No entanto, está parecendo que fazer isso via PL/SQL não seria a melhor abordagem. Acho que alguma aplicação deveria estar fazendo esse trabalho ;-)

Abraços

Legatti

Carlos Pacheco disse...

Obrigado Eduardo,
A ideia era cada vez que a minha actual aplicação cria um destes documentos na conta corrente de clientes (e às vezes são centenas de uma só vez 'em batch') gerar o respectivo hash, pois sou obrigado a guarda-lo junto com o registo para depois emitir os mesmos documentos com uma parte desse hash impresso! Toda a aplicação está feita em pl/sql incluindo a geração automatica dessas facturas e eu pensei em criar um procedimento a seguir (e antes da impressão) que gerasse esses hash's
Acha que deveria faze-lo de alguma outra forma?

Obrigado mais uma vez
Carlos

Eduardo Legatti disse...

Olá Carlos,

Acredito que seja isso mesmo. Bom, se a sua aplicação está escrita em PL/SQL então você vai ter que "quebrar a cabeça" para por isso para funcionar em PL/SQL ;-). Existe também a possibilidade de criar funções em JAVA que faça isso. Você pode pesquisar por "SHA1, SHA256, SHA512 in Oracle for free without using DBMS_CRYPTO". No mais, se nada disso der certo, talvez outro caminho seja chamar algum Web Service que faça essa autenticação.

Boa sorte!

Legatti

Carlos Pacheco disse...

Obrigado pelas 'dicas' Eduardo!
Vou tentar seguir um desses caminhos.
Um grande abraço

Carlos

Agnus Tecnologia disse...

Boa tarde Legatti,

Seguinte neste cenário, você colocou o SQL abaixo que contem a senha verdadeira do usuario.

select md5('minhasenha') from dual;

No caso, na minha aplicação Client/Server, seria possível alguem capturar o SQL que aplicação esta conversando com o Servidor de Banco de Dados?

É mais seguro ao inves de passar o valor literal eu passar parametro? Mesmo assim preciso de alguma segurança extra para proteger a "conversa" entre a aplicação e o Oracle?

Obrigado!

Agnus Tecnologia disse...

Boa tarde Legatti,

Neste cenário, o SQL usado para validar a senha é este abaixo:

select 'TRUE' from dual where '7C67E713A4B4139702DE1A4FAC672344' = md5('minhasenha');

Todavia o comando SQL contem a senha verdadeira do usuario. Como eu protego a "conversa" entre a aplicação e o Banco de Dados?

Alguem poderia capturar o SQL (texto) gerado pela aplicação. E você acha mais seguro passar parametro ao inves do valor literal da senha?

Eduardo Legatti disse...

Olá Alessandro,

O artigo foi mais uma demonstração de como gerar um valor hash MD5 usando uma função do banco de dados Oracle. Pelo que você está querendo fazer, usar uma bind variable seria mais seguro, mas mesmo assim ainda estaria vulnerável. O ideal é a sua aplicação gerar o hash MD5 sem qualquer conexão com o banco de dados. Após a geração do MD5 pela aplicação, aí sim, você compara o valor gerado com o que está armazenado no banco de dados. Nesse caso vai trafegar o valor hash MD5 e aí não tem perigo. Com certeza a linguagem de programação da sua aplicação deve ter uma função MD5.

Abracos,

Legatti

Postagens populares