Sécurisation à double référentiel avec JBOSS

le 10/03/2009 par Arnaud Huon
Tags: Software Engineering

On appelle sécurisation à double référentiel la mise en place de l'authentification à l'aide d'un premier référentiel et l'habilitation à l'aide d'un second. Cet article s'attardera sur la manière de mettre en place ce type de mécanisme pour les accès d'une application déployée sous Jboss, à l'aide des propriétés natives du serveur de RedHat.

Une petite histoire

L'entreprise IonoSport possède un portail d'entreprise, déployé sous JBoss. Il a été choisi initialement que la sécurité de ce portail se ferait à l'aide des mécanismes natifs proposés par le serveur d'application, basés sur JAAS (plus d'infos ?). Chez IonoSport,  la gestion des identités (ou GDI) a été déléguée à une base de données. L'authentification et l'habilitation du portail sont donc réalisées à l'aide des informations qui y sont stockées.

Architecture de sécurité initiale

Un beau jour, IonoSport se fait racheter par l'entreprise StellarSport. Plus gros, StellarSport a mis en place sa GDI à l'aide d'un annuaire LDAP.  Il a été décidé que seules les identités de la base de données d'IonoSport seraient migrées dans l'annuaire, pour éviter tout conflit de nommage sur les rôles. Il y aura déjà suffisamment de problèmes avec la conservation de l'unicité des uid (unique identifier)... Et le portail d'entreprise d'IonoSport dans tout ça, me direz-vous ? Comme il a aussi été décidé qu'IonoSport conserverait sa propre identité, il va continuer d'exister. Mais alors, comment vont-ils pouvoir y gérer la sécurité de leurs applications web si les identifiants sont dans un annuaire et les rôles dans une base de données ?

Architecture de sécurité cible

Et bien, avec JBoss, c'est possible !

Au préalable...

Ce qui suit concerne un environnement J2EE 6 et un JBoss 4.2.3. C'est une base HSQLDB (plus d'infos ?)qui est utilisé, du nom de xdb. Elle possède une table UTILISATEUR, avec deux champs, LOGIN et GROUPE. En ce qui concerne l'annuaire, la seule chose importante à savoir pour ce qui suit est que les utilisateurs se trouvent dans la branche ou=persons,dc=stellarsport,dc=com, et qu'ils sont définis par un uid (leur identifiant) et un userPassword (leur mot de passe).

Configuration JBoss

Configuration de la DataSource

Puisque Jboss doit avoir accès à la base de données, les développeurs d'IonoSport ont vérifié qu'une datasource y était bien configurée. Pour ce faire, ils ont vérifié qu'il existait bien un fichier hsqlIonoSport-ds.xml dans le répertoire deploy du serveur, avec le contenu suivant :

<?xml version="1.0" encoding="UTF-8"?> <datasources> <local-tx-datasource> <!-- le nom de l'objet qui sera récupéré par les applications --> <jndi-name>hsqlIonoSportDS</jndi-name> <!-- url d'accès à la base de données --> <connection-url>jdbc:hsqldb:hsql://localhostxdb</connection-url> <!-- classe du driver utilisé. Cette librairie doit être située dans le répertoire lib du serveur d'application --> <driver-class>org.hsqldb.jdbcDriver</driver-class> <!-- nom d'utilisateur et mot de passe d'accès --> <user-name>sa</user-name> <password></password> </local-tx-datasource> </datasources>

Si tout fonctionne bien, au redémarrage du serveur, le log suivant devrait apparaitre :

18:19:36,655 INFO  [ConnectionFactoryBindingService] Bound ConnectionManager 'jboss.jca:service=DataSourceBinding,name=hsqlIonoSport'

Configuration de sécurité

Passons au vif du sujet : la configuration de sécurité de JBoss. Tout se passe dans le fichier login-config.xml du répertoire conf du serveur d'IonoSport. Il est possible d'y voir le paramétrage des différents modules de supervision de JBoss, ainsi que la configuration de sécurité « par défaut ». Voici ce qu'ils y ont ajouté chez IonoSport :

<application-policy name="secuPortailIonoSport"> <authentication> <login-module code="org.jboss.security.auth.spi.LdapLoginModule" flag="required"> <module-option name="password-stacking">useFirstPass</module-option> <module-option name="java.naming.factory.initial">com.sun.jndi.ldap.LdapCtxFactory</module-option> <module-option name="java.naming.provider.url">ldap://localhost:13295</module-option> <module-option name="java.naming.security.authentication">simple</module-option> <module-option name="principalDNPrefix">uid=</module-option> <module-option name="principalDNSuffix">,ou=persons,dc=stellarsport,dc=com</module-option> <module-option name="allowEmptyPasswords">false</module-option> </login-module> <login-module code="org.jboss.security.auth.spi.DatabaseServerLoginModule" flag="required"> <module-option name="password-stacking">useFirstPass</module-option> <module-option name="dsJndiName">java:hsqlIonoSportDS</module-option> <module-option name="rolesQuery"> SELECT GROUPE, 'Roles' FROM UTILISATEUR WHERE LOGIN = ? </module-option> </login-module> </authentication> </application-policy>

Un peu d'explication s'impose  :

  • « secuPortailIonoSport » est le nom donné au profil de sécurité.
  • Puisque il est nécessaire d'utiliser deux sources de données différentes, il faut configurer deux « login-module » (module d'authentification/habilitation), un premier pour l'authentification via l'annuaire, le « LdapLoginModule », un second pour l'habilitation, le « DatabaseServerLoginModule » (besoin d'un autre type de module ?)
  • L'attribut « flag » à la valeur « required » indique que le processus d'authentification doit passer dans les deux modules. Si l'authentification échoue dans l'un des deux, alors Jboss considère que l'authentification est KO (sauf exception, voir point suivant) (besoin d'une autre valeur de « flag » ?)
  • L'option « password-stacking », inclue dans la configuration des deux modules, est la clé de la mise en place d'un processus de sécurité à double référentiel. En effet, à la valeur « useFirstPass », dès qu'un des login-module a réussi l'authentification, les autres ne se chargent plus que de l'habilitation.
  • Par sécurité, la configuration du « LdapLoginModule » contient l'option « allowEmptyPasswords » à la valeur « false ». En effet, certains annuaires considèrent par défaut qu'un mot de passe vide équivaut à un utilisateur « anonyme », ce qui fait que l'authentification est validé automatiquement, quel que soit le login renseigné. Cette option « allowEmptyPasswords=false » invalide ce mécanisme, quel que soit l'annuaire utilisé.
  • Chacun des modules est spécialisé. En effet, le « LdapLoginModule » ne possède que les éléments de configuration lui permettant de procéder à l'authentification, alors que le « DatabaseServerLoginModule », quant à lui, ne peut que récupérer que les rôles affectés à une personne authentifiée par le précédent login module (besoin de plus d'infos sur ces modules ?).
  • L'option « rolesQuery » du « DatabaseServerLoginModule » nécessite quelques explications. D'abord, le « ? » fait référence à l'identifiant de la personne authentifiée par le « LdapLoginModule ». Ensuite, le mécanisme de gestion des rôles de Jboss impose que la colonne dans laquelle est stocké le rôle de la personne soit affublée de l'alias « Roles » au sein de la query.

Et l'application ?

Maintenant que la configuration du serveur d'application est terminée, il ne reste plus aux développeurs d'IonoSport qu'à vérifier que celle du portail de leur entreprise est toujours valide. Elle se trouve dans deux fichiers distincts, web.xml et jboss-web.xml.

Dans jboss-web.xml, ils ont ajouté le paramètre permettant de se référer au mécanisme de sécurité configuré dans Jboss. Ce qui donne :

<security-domain>java:/jaas/secuPortailIonoSport</security-domain>

« secuPortailIonoSport » fait référence au nom donné à leur « application-policy », contenu dans le fichier login-config.xml. A noter que si ce paramètre « security-domain » n'était pas renseigné dans jboss-web.xml, la partie applicative du mécanisme de sécurité (configurée dans web.xml) utiliserait automatiquement la sécurité par défaut de Jboss, défini par le « application-policy » au nom sans équivoque de « other ».

Pour finir, voici ce qu'on peut trouver dans le fichier web.xml du portail d'IonoSport :

**<security-constraint> <web-resource-collection> <web-resource-name>Toute le portail</web-resource-name> <description>Sécurité globale du portail</description> <url-pattern>/*</url-pattern> </web-resource-collection> <auth-constraint> <role-name>*</role-name> </auth-constraint> </security-constraint>

<security-constraint> <web-resource-collection> <web-resource-name>interface admin</web-resource-name> <description>Protection interface d'admin</description> <url-pattern>/admin/*</url-pattern> </web-resource-collection> <auth-constraint> <role-name>admin</role-name> </auth-constraint> </security-constraint>

<security-role> <role-name>WebAppUser</role-name> </security-role>

<security-role> <role-name>admin</role-name> </security-role>

<login-config> <auth-method>BASIC</auth-method> <realm-name>Authentification test</realm-name> </login-config>**

Les points suivants sont notables :

  • Il y a deux types de ressources sécurisées, le portail dans sa globalité, et une restriction à cette même ressource, l'interface d'administration.
  • Le role-name « * » signifie que toute personne authentifiée et ayant un rôle (n'importe lequel) peut accéder à la ressource.
  • Les rôles appliqués à chaque ressource doivent être identifiés au préalable par un « security-role ».
  • Le mécanisme de login utilisé est du BASIC Auth -cf paramètre login-config à la valeur BASIC (besoin d'un mécanisme d'authentification par formulaire ?).

Dernières actions

Et voilà, il ne reste plus aux développeurs d'IonoSport qu'à redéployer leur application dans Jboss et à démarrer/redémarrer ce dernier. Un utilisateur s'y connectant aura maintenant le droit à un pop up d'authentification. En fonction de la validité de ses identifiants et ses rôles, il aura accès à tout ou partie de l'application.

Finalement, même si l'achat d'IonoSport par StellarSport est certainement la source de nombreuses nuits blanches pour l'ingénieur de la DSI d'IonoSport en charge de la GDI, au moins il n'aura plus à se faire de cheveux blancs pour la sécurisation du portail de son entreprise...

Conclusion

JBoss offre une certaine flexibilité en ce qui concerne la mise en place de mécanisme de sécurité.  Ainsi il intègre un fonctionnement natif permettant de ne pas développer de login-module "custom". D'un point de vue organisationnel, cela évite donc hypothétiquement de faire retourner l'application dans un cycle de développement logiciel là où finalement il n'a suffit que d'une action de configuration indépendante, réalisable par les équipes d'exploitation.