La sécurité des mots de passe

Quand on parle sécurité sous Oracle, on pense SSL, TCPS, TDE, Vault, VPD, HSM, chiffrage réseau… mais presque jamais password (mot de passe pour les plus mauvais en anglais Wink)

Eh oui, avant de commencer à s’affoler sur toutes les technos possibles, si nous commencions par la base : un mot de passe costaud, bien géré et protégé. Voilà un sujet facile à appréhender, qui parle à tout le monde et qui fait partie des bases de la sécurité, car bien souvent son importance est complètement oublié.

 

Heureusement Oracle met à disposition dans les versions  Standard One, Standard et Entreprise plusieurs protections pour les mots de passe.  Ce n’est pas la peine d’acheter la luxueuse option Oracle Advanced Security (OAS)…, merci Oracle.


Dans cet article, nous allons voir ce qu'il est possible de faire avec Oracle pour sécuriser au mieux les mots de passe. Nous allons voir :

  • Une nouveauté 11g, la nouvelle méthode de chiffrement des mots de passe et surtout comment l'activer. Nous essaierons de casser un mot de passe avant et après la mise en place de ce nouveau chiffrement
  • La politique de mot de passe contrôlé par profile.
  • Le contrôle sur la complexité du mot de passe. Comment l'activer et l'améliorer.
  • Une autre nouveauté 11g, la sensibilité à la case.
  • Le stockage des mots de passe dans un magasin de mot de passe sur les clients

 

 

Mais avant de commencer, il faut savoir que le chiffrage du mot de passe à travers le réseau du client vers le serveur est automatique même si il n’y a pas de mise en place de chiffrage de la couche sqlnet. Nous ne traiterons donc pas ici du chiffrage de la couche sqlnet, qui de plus, nécessite la licence Oracle Advanced Security et ne rajoute donc rien pour la partie sécurité des mots de passe.

 

Cet article s'applique à la version 11gR2. Les tests ont été fait sur une version 11.2.0.3.0. Le serveur de base de données est celui décrit dans l'article testbed 11gR2 RAC ASM

 

Problème sur le stockage en base du mot de passe

En 10g, le mot de passe était stocké par hashage DES, en 11g c’est du SHA-1 160 bit avec salage, mais d’après la documentation officielle (Oracle Database Security Guide 11.2.0.3 p3-13) il ne semble pas que cela soit par défaut, contrairement à ce que l’on pourrait croire en lisant  sur de nombreux blogs qui se contentent de dire qu’avec le 11g le stockage est en SHA-1. Il faut faire une action sur le sqlnet.ora du serveur  pour l’activer complètement pour tous les clients qui se connecteront à la base. Et il est vivement conseillé de l’activer…

Pour prouver l’utilité de l’activation su SHA-1, nous allons nous mettre dans la peau d’un administrateur mal intentionné qui dans un premier temps à affaire à une base de donnée compatible 10g11g (ancienne méthode de cryptage de mot de passe) et nous essaierons de casser le mot de passe par force brute. Puis, dans un second temps, nous passerons la base en compatibilité 11g exclusive  et nous essaierons à nouveau de casser le mot de passe par force brute.

Je suis administrateur, j'ai accès à une connexion sysdba sur la base, et je veux connaitre le mot de passe du compte TOTO, pour extraire des données en toute discrétion avec un compte autorisé comme ça j'échapperais à l'audit, personne ne verra rien

# sqlplus / as sysdba
SQL> select username , password_versions from dba_users where USERNAME='TOTO';

USERNAME               PASSWORD
------------------------------ --------
TOTO                   10G 11G

Parfait ils ont laissé la base en compatibilité 10G11G. Il ne reste plus qu'à espérer que le mot de passe de TOTO ne soit pas trop long. Une petite requête pour récupérer les informations qui vont m'aider à trouver le mot de passe:

SQL>  select u.name||':'||u.password||':'||substr(u.spare4,3,63)||':'||d.name||':'||  sys_context('USERENV','SERVER_HOST')||':'  from sys.user$ u, sys.V_$DATABASE d where u.type#=1 and u.name ='TOTO'

TOTO:F31BFCF50BA980E3:0ACCE22A7B611EF4EBCB363FA8AFC8E13D7B180E37168A7516491885621A:ARKONA:racdb1:

Maintenant que j'ai l'information, il n'y a plus qu'à l'injecter dans l'outil xxxxxx (trouver sur internet) et le faire tourner sur mon modeste PC avec un core i5 de début 2010.

Password found: TOTO:AZ54%T:0ACCE22A7B611EF4EBCB363FA8AFC8E13D7B180E37168A7516491885621A:ARKONA
Elpased time: 1355s
Checked passwords: 8085416026
Password / Second: 5967096

Bingo, mot de passe trouvé en moins de 23minutes! Il n' a que 6 caractères c'est pour ça : AZ54%T

Vite je teste:

# sqlplus toto/AZ54%T
SQL>

Yes, ça fonctionne!

 

Maintenant, nous allons mettre en place la base en compatibilité exclusive 11g (11g au moment de l'écriture de l'article, maintenant il faut écrire exclusive 11g et 12c, la 12c étant sortie 5 mois après l'article).

Pour cela il faut éditer le fichier sqlnet.ora sur le serveur de la base de données. Comme notre base de test est en RAC, avec le compte grid

# vi $ORACLE_HOME/network/admin/sqlnet.ora

Puis ajouter la ligne :

sqlnet.allowed_logon_version=12

Et enfin arrêter et redémarrer le listener adéquate

# srvctl stop scan_listener
# srvctl start scan_listener

Et voilà la base est en mode compatibilité exclusive 11g (et 12c), cependant les vieux mots de passe sont toujours 10g11g, il faut donc les changer

Je suis administrateur, j'ai accès à une connexion sysdba sur la base, et je veux connaitre le mot de passe du compte TOTO, pour extraire des données en toute discrétion avec un compte autorisé comme ça j'échapperais à l'audit, personne ne verra rien

# sqlplus / as sysdba
SQL> select username , password_versions from dba_users where USERNAME='TOTO';

USERNAME               PASSWORD
------------------------------ --------
TOTO                   11G

Je suis très désappointé...le mot de passe de TOTO est chiffré en SHA-1 160bit with salt. Bon tant pis je n'ai pas la puissance financière pour monter une machine capable de casse le mot de passe par force brute.

Si notre administrateur mal intentionnée avec fait sa requête, voilà ce qu'il aurait obtenu:

TOTO::08E9D479B62C71EF20125FD591FA21FB5C61A47D876666945F140CD0619C:ARKONA:racdb1:

Il lui aurait manquait le 2ème champ, celui qui est compatible 10g et facile à casser avec un PC core i5... tant que le mot de passe est inférieur à 8 caractères, au-delà il faut plus de puissance si on ne veut pas dépasser l'année à déchiffrer.

Remarque :

En 10g le mot de passe chiffré pouvait être obtenu par tous les comptes avec le rôle DBA car il était visible dans la vue DBA_USER. En 11g il n’est visible que par l’interrogation de la table  sys.user$ et donc seulement par les connexions SYSDBA. C’est donc un peu mieux qu’en 10g. Et c’est pour cela que dans l’exemple ci-dessus l’administrateur mal intentionné a dû se connecter SYSDBA, sans ce droit il n’aurait pas eu accès au mot de passe chiffré.
Le mot de passe est donc en théorie plus protégé. Cependant il ne faut pas oublier qu’il est stocké dans une table, donc dans un datafile,  qui plus est, appartenant au tablespace SYSTEM, ce qui signifie un fichier pas trop volumineux et dont le nom est très certainement de la forme system.dbf, en un mot, dans un fichier facilement identifiable. Il suffit à un administrateur qui a accès au compte root par exemple,  de le copier et de chez lui, tranquillement, le décortiquer pour trouver le mot de passe chiffré. Ensuite il ne reste plus qu’à le déchiffrer par attaque au dictionnaire ou par force brute. D’où il faut espérer que la base ait une vrai politique de mot de passe (renouvellement, vérification de complexité, taille minimum de mot de passe…).

 

Politique de mot de passe

Une bonne politique de mot de passe, ce sont des mots de passe forts et à changer régulièrement. Forts pour que l’attaque brute mette du temps à déchiffrer et quand elle a enfin fini de déchiffrer, trop tard le mot de passe a changé.

Oracle met à disposition la possibilité de gérer cela par profil. Chaque profil est un ensemble de règle, et à chaque user Oracle est associé un profil. Ces règles sont définis par des variables, interrogeable avec le vue DBA_PROFILES.

Pour obtenir tous les paramètres des mot de passe par profil :

SQL> select PROFILE,RESOURCE_NAME,LIMIT from dba_profiles where RESOURCE_TYPE='PASSWORD' order by 1,2;
PROFILE                        RESOURCE_NAME                    LIMIT
------------------------------ -------------------------------- -------------------------
DEFAULT                        FAILED_LOGIN_ATTEMPTS            10
DEFAULT                        PASSWORD_GRACE_TIME              7
DEFAULT                        PASSWORD_LIFE_TIME               180
DEFAULT                        PASSWORD_LOCK_TIME               1
DEFAULT                        PASSWORD_REUSE_MAX               UNLIMITED
DEFAULT                        PASSWORD_REUSE_TIME              UNLIMITED
DEFAULT                        PASSWORD_VERIFY_FUNCTION         NULL

 

Sans lire la documentation les noms sont assez parlant. Le résultat de la requête ci-dessus, est le résultat obtenu sur une base par défaut au niveau des profils. Vous pouvez constater que par défaut tous les users auront un mot de passe :

  •  valide pendant 180 jours (PASSWORD_LIFE_TIME)
  • qui se bloque après 10 tentatives d’échec consécutives (FAILED_LOGIN_ATTEMPTS)
  • qui reste bloqué 1 jours (PASSWORD_LOCK_TIME) quand il se bloque
  • qui quand il arrive à expiration, laisse 7 jours (PASSWORD_GRACE_TIME)  pour le changer. Après ces 7 jours, le mot de passe doit être obligatoirement changé.
  • qui pourra être réutilisé autant de fois que l’user le souhaite (PASSWORD_REUSE_MAX)
  • qui pourra être réutilisé dès le prochain changement de mot de passe ( PASSWORD_REUSE_TIME).
  • qui ne sera pas vérifié au niveau complexité lors de sa modification (PASSWORD_VERIFY_FUNCTION = NULL)

La création d’un nouveau profil se fait par simplement comme dans l’exemple ci-dessous :

SQL> create profile prof_appli1 limit PASSWORD_LIFE_TIME 90  FAILED_LOGIN_ATTEMPTS 3;

Ils  sont facilement modifiables avec une commande alter :

SQL> alter profile prof_appli1 PASSWORD_LIFE_TIME 60 ;

Et facilement assignable à un (ou plusieurs utilisateurs):

SQl> alter user maurice profile prof_appli1 ;

 

Le dernier paramètre : PASSWORD_VERIFY_FUNCTION introduit la gestion de la complexité du mot de passe, il prend comme valeur NULL ou bien le nom de la fonction de vérification.

 

Gestion de la complexité du mot de passe

Par défaut donc le vérificateur de complexité du mot de passe n’est pas activé et n’est pas installé !

Pour l’installer ce n’est pas bien compliqué :

# sqlplus / as sysdba
SQL> @$ORACLE_HOME/rdbms/admin/utlpwdmg.sql

Et voilà il est installé,  et le profil par défaut : default, a son paramètre PASSWORD_VERIFY_FUNCTION égal à VERIFY_FUNCTION_11G, le nom de la fonction qui vient d’être installée :

SQL> select * from dba_profiles where PROFILE='DEFAULT' and RESOURCE_NAME='PASSWORD_VERIFY_FUNCTION'
PROFILE                    RESOURCE_NAME                          RESOURCE LIMIT
-------------------- -------------------------------- -------- --------------------
DEFAULT                  PASSWORD_VERIFY_FUNCTION              PASSWORD VERIFY_FUNCTION_11G

 

Le vérificateur vérifie alors que :

  • Le mot de passe doit contenir plus de 8 caractères (max 30)
  • Le mot de passe ne doit pas être le même que le nom du compte, ni être épellé en arrière ou alors avoir juste une chaine numérique en annexe.
  • Le mot de passe ne doit pas être le même que le nom du serveur ou le nom du serveur avec les numéros de 1 à 100 annexées.
  • Le mot de passe doit contenir au moins 1 caractère numérique et un alphabétique.
  • Le mot de passe doit différer d’au moins 3 caractères de l’ancien.
  • Quelques mots de passe proscrits sont vérifiés. 

Il est conseillé par Oracle de faire son propre développement  pour augmenter la complexité du vérificateur.

Si vous ouvrez le script de création de la fonction de vérification, vous allez vite constater qu’effectivement Oracle a raison, le vérificateur a grandement besoin d’être amélioré.

Exemple, dans le code voici la liste des quelques mot de passe préscrit

   IF NLS_LOWER(password) IN ('welcome1', 'database1', 'account1', 'user1234', 'password1', 'oracle123', 'computer1', 'abcdefg1', 'change_on_install')
   THEN
      raise_application_error(-20006, 'Password too simple');
  END IF;

 

Il est intéressant de mettre en place une « vrai » vérification contre l’usage de mot du dictionnaire, pour éviter les attaques hybrides dictionnaire/force brute. En effet si nous mettons le vérificateur en place alors il y aura des caractères alpha-numérique, il ne pourra donc pas y avoir un mot de passe identique à un mot du dictionnaire. Mais dans une attaque hybride on teste des mots du dictionnaire combinés à des caractères numériques et spéciaux. Pour se protéger de ce type d’attaque, nous allons mettre en place un contrôle qui vérifie que le mot de passe ne contient aucun mot du dictionnaire de plus de 3 caractères.

Cette mise en place s'inspire de l'article Securing Your Database: A simple Brute Force Blocker. Le contrôle a été amélioré, car dans l'article initial, il n'y avait qu'un contrôle sur le mot de passe entier qui ne devait pas être contenu dans un dictionnaire, or cela ne sert à rien car nous forçons la présence de caractère numérique.

Mise en place:

Téléchargez un dictionnaire au format texte. Dans notre exemple nous prenons le dictionnaire déjà présent /usr/share/dict/linux.words mais en anglais.

Avec le compte SYSTEM, créez la table qui va contenir le dictionnaire dans la base

SQL>create table system.dictionnaire ( mot varchar2(4000));

Pour le chargement, nous allons utiliser le bon vieux sqloader.

Créez le fichier de contrôle (dictionnaire.ctl) et ajoutez les lignes suivantes:

LOAD DATA
infile "/usr/share/dict/linux.words"
REPLACE INTO TABLE system.dictionnaire
(mot char(4000))

 Puis le fichier de paramètre(dictionnaire.parfile)

CONTROL=dictionnaire.ctl
LOG=dictionnaire.log
BAD=dictionnaire.bad
DISCARD=dictionnaire.dsc
ERRORS=0

Chargez alors en base:

# $ORACLE_HOME/bin/sqlldr control=dictionnaire.ctl parfile=dictionnaire.parfile

Les 479829 mots du dictionnaire sont alors en base. Nous supprimons alors tous les mots inférieurs ou égal à 3 caractères et ajoutons un index.

SQL> delete system.dictionnaire  where LENGTH(mot) < 4;
SQL> commit;
SQL> create index system.dictionnaire on system.dictionnaire(mot);

Faites une copie du fichier $ORACLE_HOME/rdbms/admin/utlpwdmg.sql. Nous allons le modifier pour y rajouter notre contrôle de chaine de caractère.

Dans la partie variable de la fonction VERIFY_FUNCTION_11G, ajoutez les variables ci-dessous:

v_table_matches number;
v_word_count number;
v_use_dictionary boolean;
v_pw_matches number;

Supprimez tout le code compris entre ce commentaire

--First, check that the dictionary table exists

et celui-ci:

 -- Check if the password is the same as oracle (1-100)

 Et le remplacez par le code ci-dessous:

v_use_dictionary := false;

select count(*) into v_table_matches
from dba_tables
where table_name='DICTIONNAIRE' and owner='SYSTEM';

if v_table_matches = 1 then
    select count(*) into v_word_count
    from system.dictionnaire ;
    if v_word_count > 0 then
        v_use_dictionary := true;
    end if; --if v_word_count > 0
end if; --if v_table_matches=1 (table count)

if v_use_dictionary then
    --If dictionary exists, check the password against it
    select count(*) into v_pw_matches
    from system.dictionnaire p
    where NLS_LOWER(password) like NLS_LOWER('%'||p.mot||'%');
        if v_pw_matches != 0 then
        raise_application_error(-20006, 'Password found in dictionary');
        end if;
else
    --otherwise, make a simple check
   IF NLS_LOWER(password) IN ('welcome1', 'database1', 'account1', 'user1234', 'password1', 'oracle123', 'computer1', 'abcdefg1', 'change_on_install')
   THEN
      raise_application_error(-20006, 'Password too simple');
   END IF;
end if; --if v_use_dictionary

Relancez l'installateur de vérification de complexité de mot de passe:

# sqlplus / as sysdba
SQL> @$ORACLE_HOME/rdbms/admin/utlpwdmg.sql

Testons cela de suite

# sqlplus system/manager
 SQL> alter user toto identified by "1pass4df$DD";
alter user toto identified by "1pass4df$DD"
*
ERROR at line 1:
ORA-28003: password verification for the specified password failed
ORA-20006: Password found in dictionary

Parfait, le vérificateur a vu qu'il y a un mot contenu dans le dictionnaire dans le mot de passe, testons alors avec un mot de passe qui ne contient aucun mot contenu dans le dictionnaire

SQL> alter user toto identified by "1pa4df$DDss";
User altered.

Parfait. Il ne reste plus qu'à tester, que si le user TOTO veut changer par lui-même le mot de passe, il y aura le même comportement du vérificateur

# sqlplus toto/1pa4df$DDss
SQL> password
Changing password for TOTO
Old password:             
New password:                   -->1pass123456
Retype new password:
ERROR:
ORA-28003: password verification for the specified password failed
ORA-20006: Password found in dictionary

Password unchanged

SQL> password
Changing password for TOTO
Old password:   
New password:             -->56pas56$s
Retype new password:
Password changed

Parfait, cela semble bien fonctionner.

 

Sensibilité à la case

Une des nouveautés de la 11g, qui renforce la complexité des mots de passe, et que le mot de passe est sensible à la case (case sensitive). Et c’est le comportement normal à la création d’une base, il n’y a rien à activer.

Si vous ne le souhaitez pas :

SQL> alter system set SEC_CASE_SENSITIVE_LOGON=FALSE ; -- pas bien

Remarque:  Dans l'exemple de l'administrateur mal intentionné, il a découvert le mot de passe AZ54%T et a réussit à se connecter, car la base n'était pas case sensitive et le vrai mot de passe était az54%t. Si la base avait été case sensitive, il aurait eu 8 combinaisons maximum à tester pour se connecter et y serait arrivé quand même que le maximum de tentative autorisé par défaut est 10.

Idel pour le fichier de mot de passe orapw, la sensibilité à la case a été introduite, et elle est activé par défaut. Si vous ne le souhaitez pas :

# orapwd file=orapwARKONA entries=10 ignorecase=y  ## pas bien

 

Problème du passage de mot de passe à la connexion

Comme nous l’avons vu dans l’introduction, le passe du mot de passe à travers le réseau est chiffré. Mais souvent un des problèmes de sécurité au moment de la connexion, est la chaine de connexion elle-même. Comment gérer le fait que le mot de passe ne soit pas écrit en « dur » quelque part dans l’application pour sa connexion, soit l’application sait gérer cela par des mécanismes de chiffrage déchiffrage, soit Oracle peut le faire pour vous grâce aux magasins de mot de passe externes (External Password Store, c’est toujours plus classe en anglais quand mêmeLaughing).

Nous allons mettre en place un "external password store", c'est extrêmement simple.

Sur un serveur client

[winroc@client winroc]$  mkdir /oracle/wallet_11g
[winroc@client wallet_11g]$  cd /oracle/wallet_11g
[winroc@client wallet_11g]$  mkstore -wrl /oracle/wallet_11g -create
Oracle Secret Store Tool : Version 11.2.0.1.0 - Production
Copyright (c) 2004, 2009, Oracle and/or its affiliates. All rights reserved.

Enter password:             

Enter password again:    

 

Le mot de passe que l'on vous demande est celui du wallet qui va stocker votre magasin de mot de passe.

Ensuite il faut remplir le magasin:

[winroc@client wallet_11g]$  mkstore -wrl /oracle/wallet_11g -createCredential ARKONA system
Oracle Secret Store Tool : Version 11.2.0.1.0 - Production
Copyright (c) 2004, 2009, Oracle and/or its affiliates. All rights reserved.

Your secret/Password is missing in the command line
Enter your secret/Password:         

Re-enter your secret/Password:         

Enter wallet password:             

Create credential oracle.security.client.connect_string1

Nous venons de mettre le mot de passe oracle du compte SYSTEM de la base de données de ARKONA

Maintenant toujours sur notre client, dans le client oracle, éditer le fichier sqlnet.ora (ou créer le cas échéant) dans le répertoire $ORACLE_HOME/network/admin et ajouter:

WALLET_LOCATION =
    (SOURCE =
        (METHOD = FILE)
        (METHOD_DATA =
         (DIRECTORY = /oracle/wallet_11g)
        )
    )
SQLNET.WALLET_OVERRIDE = TRUE

 

Et voilà, plus la peine de passe le mot de passe de SYSTEM pour se connecter à partir de ce client,

# sqlplus /@ARKONA
SQL> select user from dual;
USER
--------------------
SYSTEM

Avec  "/@ARKONA" nous nous connectons SYSTEM sur ARKONA sans à avoir à passer le mot de passe.

L’inconvénient est que l’on ne peut stocker qu’un login/password par base, mais on peut y stocker plusieurs entrées (bases)

Le plus gros inconvénient reste le vol possible du wallet. Si quelqu’un vol le fichier alors il pourra se connecter à partir d’une autre machine avec le login/password contenu dans le magasin. Il ne pourra pas en revanche consulter le contenu du wallet car il n’aura pas le mot de passe du wallet et donc il ne connaitra pas à priori quel alias il faut passer (ARKONA dans notre exemple). Si nous voulons éviter le vol il faudra passer par un HSM.

 

Conclusion

La sécurisation du mot de passe ne coûte pas cher, elle ne nécessite pas de licence additionnel. De plus, comme nous venons de le voir, elle n’est pas compliquée à mettre en œuvre. Il n’y a donc aucune raison de faire l’impasse.

Pour aller un plus loin, je vous conseil un excellent article Hacker les mots de passe d'Oracle 11g de The ArKZoYd Compagny, avec des liens sur des sites plus technique mais en anglais (damned!!!). Il y est expliqué comment récupérer simplement en 11g le mot de passe "hash-er" qui passe sur le réseau, reste ensuite bien sûr l'attaquer en force brute et donc cela dépendra de la faiblesse du mot de passe mais aussi de la politique du mot de passe.