Quelles sont les faiblesses de l’encryption des mots de passe Oracle ? Comment est-ce qu’Oracle crée les clés de hachage afin d’en améliorer la sécurité ? C’est ce que je vous propose de découvrir dans ce post.

Le mot de passe est la forme la plus commune d’authentification. Ce dernier est stocké dans une table Oracle sous la forme d’une clé de hachage. Lorsqu’un utilisateur tente de se connecter, le mot de passe saisi est alors crypté (avec le même algorithme de cryptage) puis comparé à celle stocké dans la base pour identification.

Beaucoup d’administrateurs de bases de données connaissent la vue DBA_USERS qui contient les informations d’utilisateur (Nom, mot de passe, verrouillage du compte etc.). Mais depuis la version 11g, le champ password n’est plus renseigné. Il est cependant toujours possible de l’obtenir grâce à la table SYS.USER$. De plus, ce dernier a été rendu “case sensitive”. Un prochain posting sera publié tout spécialement à ce sujet. Apparemment, Oracle prévoit de renforcer la sécurité des mots de passe et de rendre moins visible les clés.

Comment peut-on considérer un mot de passe comme sécurisé ? Cela dépend des éléments suivants :

  • La chaîne aléatoire ajouté à la clé (= salt)
  • Le nombre d’itérations de l’encryption
  • La longueur de la clé

1. La chaîne aléatoire ajouté (= Salt)

Voici un petit descriptif des termes employés dans cet article :

  • Clé : désigne la chaîne transformée qui sera cryptée par la clé de cryptage
  • Clé de cryptage : la chaîne qui sera utilisée pour crypter
  • Clé de hachage : la chaîne de résultat après cryptage

Il est évident qu’Oracle utilise la combinaison de la chaîne de connexion et du mot de passe pour créer sa clé de hachage. Si on prend l’exemple de la concaténation sys et manager, celle-ci donne toujours la clé de hachage suivante 5638228DAF52805F quel que soit le serveur, le système d’exploitation ou la version d’Oracle. C’est un moyen facile de détecter si les mots de passe par défauts sont toujours utilisés ou si un mot de passe par défaut a été défini. Pour trouver un mot de passe, il suffit donc de générer les clés de hachage pour un utilisateur donné avec toutes les combinaisons possibles de mots de passe, puis de les comparer à la table des mots de passe. Si les clés de hachage correspondent, vous avez obtenu son mot de passe. De plus, en créant un utilisateur « sy » et un mot de passe « smanager », la clé de hachage obtenue est identique à la clé de hachage générée par sys et manager, comme démontré ci-dessous :

SQL> create user sy identified by smanager;Utilisateur crée.SQL> select password from user$ where name='SY';PASSWORD
------------------------------
5638228DAF52805F

 

Cet exemple démontre qu’il y a bien concaténation de l’utilisateur et du mot de passe.
Ce mécanisme est aussi vrai pour les minuscules et majuscules. Donc, toutes les variantes suivantes donneront cette même clé :

  • SyS MaNaGer
  • SYS MANAGER
  • Sys Manager
  • SYS manager
  • sys MANAGER

La clé se compose de cette manière ou du moins contient ces éléments :

upper(user||password)

Cependant, ce n’est pas le seul critère nécessaire pour générer une clé de hachage valide. En effet, si l’on tente de générer une clef de hachage à l’aide de cette seule règle, les erreurs suivantes nous serons retournées par Oracle.

ORA-28232: longueur d'entrée non valide pour le toolkit Obfuscation
ORA-06512: à "SYS.DBMS_OBFUSCATION_TOOLKIT_FFI", ligne 21
ORA-06512: à "SYS.DBMS_OBFUSCATION_TOOLKIT", ligne 99
ORA-06512: à ligne 25
28232. 0000 -  "invalid input length for obfuscation toolkit"
*Cause:    Length of data submitted for encryption or decryption is not a
multiple of 8 bytes.
*Action:   Make sure that the length of the data to be encrypted or decrypted
is a multiple of 8 bytes.

La clé doit être composée d’un multiple de 8 bytes pour que le package dbms_obfuscation_toolkit fonctionne. Apparemment, la clé est complétée de vide, ce qui donnerait au final ceci :

SYSMANAGER (en tout 16 caractères)

Voici le résultat (en utilisant le package dbms_obfuscation_toolkit.DESEncrypt qui est développé dans le prochain chapitre) :

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
S Y S M A N A G E R ‘ ‘ ‘ ‘ ‘ ‘ ‘ ‘ ‘ ‘ ‘ ‘
E F 1 3 E 4 9 C 8 1 6 1 D 0 2 F

Ce qui ne correspond pas à la clé de hachage 5638228DAF52805F qui est stocké dans la table USER$ (par contre, elle est bien composée de 16 positions). Il y a bien un mécanisme manquant pour obtenir la clé. La solution m’a été donnée par Pete Flannigan http://www.petefinnigan.com.

En réalité, il ne s’agit pas de ” ” (= vide) mais du caractère CHR(0). Si les deux retournent bel et bien un blanc visuellement. Le code ascii n’est pas identique, car l’un retourne NULL et l’autre 0 comme le montre le tableau ci-dessous :

SQL> select '' "blanc",ascii(''),chr(0) "chr(0)",ascii(chr(0)) from dual;

blanc    ASCII('')  chr(0)  ASCII(CHR(0))
------  ---------- -------  -------------
0

De plus, la clé est également composée du caractère chr(0) entre chaque lettre qui compose l’utilisateur et son mot de passe. Voici le résultat final :

‘S’||chr(0)||’Y’||chr(0)||’S’||chr(0)||’M’||chr(0)||’A’||chr(0)||’N’||chr(0)||’A’||chr(0)||’G’||chr(0)||’E’||chr(0)||’R’||chr(0)||chr(0)||chr(0)||chr(0)||chr(0) (en tout 24 caractères)

Maintenant, la clé se compose de 24 bytes, parce qu’il faut toujours obtenir une chaîne qui est un multiple de 8. Il y a 10 bytes (pour la chaine utilisateur et mot de passe), plus 9 bytes pour les Chr(0) qui séparent chaque lettre de la chaìne. Ce qui donne un total de 19 bytes. Il manque donc 5 bytes pour compléter la clé.
Voici un script qui permet la construction de cette chaîne en fournissant la concaténation du nom de l’utilisateur et son mot de passe :

-- Source de Pete Flannigan

Declare
vString varchar2(32):=upper('SYSMANAGER');
vLett char(1);
vLeng number;
vCompo varchar2(32);
vModul number;
vRest number;

Begin
vLeng:=length(vString);
for i in 1..vleng
loop
vlett:=substr(vString,i,1);
vCompo:=vCompo||chr(0)||vLett;
end loop;
vModul:= mod((vLeng*2),8);
if (vModul = 0) then
vRest:= 0;
else vRest:=8 - vModul;
end if;
for i in 1..vRest
loop
vCompo:=vCompo||chr(0);
end loop;
dbms_output.put_line(vCompo);
end;
/

2. Le nombre d’itérations de l’encryption

Oracle utilise le package dbms_obfuscation_toolkit.DESEncrypt pour crypter le mot de passe.
Le package nécessite 2 paramètres d’entrées en raw et ce dernier retourne une valeur en raw qui est la clé de hachage. Le premier paramètre est la chaine à crypter (la clé, celle qui a été construite dans le chapitre précédent). Le deuxième est la clé de cryptage qui est une suite de 16 charactères de 0 à F, voici la chaîne:

‘0123456789ABCDEF’

Ce qui donnerait comme fonction de cryptage ceci :

-- Source de Pete FlanniganvString varchar2(32):='SYSMANAGER';
vLett char(1);
vLeng number;
vCompo varchar2(32);
vModul number;
vRest number;
vPwd raw(128);
vKeyRaw1 raw(128):=hextoraw('0123456789ABCDEF');
vKeyRaw2 raw(128);
vPwdHash raw(2048);
vHexstr varchar2(2048);
vPwdHashStr varchar2(2048);

Begin
vLeng:=length(vString);
for i in 1..vleng
loop
vlett:=substr(vString,i,1);
vCompo:=vCompo||chr(0)||vLett;
end loop;
vModul:= mod((vLeng*2),8);
if (vModul = 0) then
vRest:= 0;
else vRest:=8 - vModul;
end if;
for i in 1..vRest
loop
vCompo:=vCompo||chr(0);
end loop;
vPwd:=utl_raw.cast_to_raw(vCompo);

dbms_obfuscation_toolkit.DESEncrypt(input =>vPwd,key=>vKeyRaw1,encrypted_data =>vPwdHash);
vHexstr:=rawtohex(vPwdHash);
vLeng:=length(vHexstr);
vPwdHashStr:=substr(vHexstr,(vLeng-16+1),16);
dbms_output.put_line('Voici la clé: '||vPwdHashStr);
end;
/
Voici la clé: 48F34A5A3E8C2ED8 
Procédure PL/SQL terminée avec succès.

 
Ce n’est pas vraiment le résultat attendu !
Simplement, parce que toute clé efficace est cryptée plusieurs fois, sinon, elle n’est pas efficace. Cependant, un nombre trop important d’itérations de cryptage serait fatal pour le temps de login qui serait largement ralongé (pour décryptrer la clé, justement). Oracle a choisi le cryptage à deux niveaux. Cette fois-ci, la clé de cryptage utilisée est la clé de hachage obtenue après le premier cryptage que nous venons d’obtenir :

-- Source de Pete FlanniganvString varchar2(32):='SYSMANAGER';
vLett char(1);
vLeng number;
vCompo varchar2(32);
vModul number;
vRest number;
vPwd raw(128);
vKeyRaw1 raw(128):=hextoraw('0123456789ABCDEF');
vKeyRaw2 raw(128);
vPwdHash raw(2048);
vHexstr varchar2(2048);
vPwdHashStr varchar2(2048);

Begin
vLeng:=length(vString);
for i in 1..vleng
loop
vlett:=substr(vString,i,1);
vCompo:=vCompo||chr(0)||vLett;
end loop;
vModul:= mod((vLeng*2),8);
if (vModul = 0) then
vRest:= 0;
else vRest:=8 - vModul;
end if;
for i in 1..vRest
loop
vCompo:=vCompo||chr(0);
end loop;
vPwd:=utl_raw.cast_to_raw(vCompo);
dbms_obfuscation_toolkit.DESEncrypt(input =>vPwd,key=>vKeyRaw1,encrypted_data =>vPwdHash);
vHexstr:=rawtohex(vPwdHash);
vLeng:=length(vHexstr);
vKeyRaw2:=hextoraw(substr(vHexstr,(vLeng-16+1),16));
dbms_obfuscation_toolkit.DESEncrypt(input =>vPwd,key=>vKeyRaw2,encrypted_data =>vPwdHash);
vHexstr:=rawtohex(vPwdHash);
vLeng:=length(vHexstr);
vPwdHashStr:=substr(vHexstr,(vLeng-16+1),16);
dbms_output.put_line('Voici la clé: '||vPwdHashStr);
end;
/

Voici la clé: 5638228DAF52805F 
Procédure PL/SQL terminée avec succès.

On obtient bien la bonne clé qui est 5638228DAF52805F.

Ce package ne permettra pas de connaître le mot de passe, mais de détecter si ce dernier est suffisamment sécurisé. Par exemple, ce qui se pratique couramment : UserName=Password. Il donc très important d’avoir des mots de passe suffisamment complexes et longs pour éviter le décryptage par une attaque de type « Brute Force Attack ». Un simple ordinateur personnel peut lancer 830’000 tentatives de décryptage de mots de passe à la seconde. Ceci reste faible en proportion du nombre de combinaisons possibles pour autant qu’un mot de passe judicieux ait été choisi.

3. La longueur de la clé

Il est très simple de mettre en place un système qui vérifie la complexité des mots de passe saisis afin de ne pas permettre des clés trop simples ou courtes. Pour se faire, il faut installer le package utlpwdmg.sql :

SQL> @$ORACLE_HOME/rdbms/admin/utlpwdmg.sql
Fonction créée.
Profil modifié.
Fonction créée.

Une fois ce package installé, il n’est plus autorisé de mettre un mot de passe identique au nom d’utilisateur ou un mot de passe plus petit que 8 caractères.

SQL> create user testpwd identified by testpwd;
create user testpwd identified by testpwd
*
ERREUR à la ligne 1 :
ORA-28003: Échec de la vérification du mot de passe indiqué
ORA-20001: Password length less than 8

4. Conclusion

Ce posting met en évidence le mécanisme de cryptage mis en place par Oracle et présente certains points faibles de ce type de cryptage. Même si Oracle a fait de grands efforts dans ce domaine (par exemple, rendre le mot de passe “case sensitive”).
Aujourd’hui la sécurité des données est devenue un thème crucial. Pour s’en rendre compte, il suffit de voir dans l’actualité les nombreuses attaques ayant ciblé en particulier le domaine bancaire ces derniers mois. En général, ces attaques ne viennent pas de l’extérieur, mais bien de l’intérieur des entreprises. Il est donc important de sensibiliser les DBA mais également les managers de manière à tout mettre en place pour sécuriser au mieux leurs infrastructures de bases de données.
Une première approche consistant à la mise en place de best practices telles que celles décrites ci-dessous permettent d’éviter un bon nombre d’attaques :

  • Sécuriser l’accès à la table USER$
  • Sécuriser l’accès à certains packages
  • Auditer les tables sensibles
  • Mettre en place le package de vérification de mots de passe