mercredi 31 août 2011

Capitaine Spock et les tests unitaires

Capitain SpockSpock est framework open source de test qui permet de définir une spécification de test. L'approche est plus structurée que Junit. Je vous laisse découvrir :


Article rédigé pour la plateforme de capitalisation technique de Sword

lundi 11 juillet 2011

Authentification sur LDAP avec SpringSecurity

Spring Security est un projet de Spring Source anciennement appelé Acegi Security. Le framework a beaucoup évolué depuis en s'étoffant de nombreux connecteurs et en améliorant son intégration avec Spring framework.

Dans cet article, nous allons aborder la méthode d'authentification LDAP (Windows Active Directory, Open LDAP...) à travers Spring Security. Les détails de l'utilisation de l'autorisation (droit d'effectuer certaines opérations) ne seront pas évoqués ici, le sujet étant déjà largement documenté. La référence du framework comprend tout un chapitre dédié à l'interaction avec un LDAP.

Les annuaires LDAP

Un annuaire LDAP est un système de gestion des informations associées aux acteurs d'un SI (utilisateurs, machines, équipements électroniques...). Le principal rôle de l'annuaire est de stocker les informations des utilisateurs et de permettre grâce à des mécanismes simples de rechercher, tri et organiser l'information.
Les annuaires LDAP proposent une organisation hiérarchique de l'information. Chaque élément est contenu dans les éléments parents (groupes, unités organisationnelles ou physiques...).
La fonctionnalité qui nous intéresse est la possibilité d'authentifier les utilisateurs. L'annuaire LDAP possède le login de l'utilisateur et le password (hashé), il peut ainsi authentifier de manière sûre un utilisateur. Les annuaires ont aussi la possibilité d'enregistrer une liste de rôles attribués aux utilisateurs. Ces rôles permettent de différencier des profils d'utilisateurs pour les offrir des autorisations différentes.
LDAP n'est pas une implémentation d'annuaire, mais uniquement une norme respectée par de nombreux grands systèmes d'annuaires (Windows Active Directory, Open LDAP, Apache Directory ServerNovell eDirectoryIBM Lotus Domino...).
Dernier point d'importance, LDAP dispose d'une règle de nommage permettant de gérer les organisations hiérarchiques. Par exemple, le Distinguished Name (DN) cn=Jean,ou=gens,dc=EXEMPLE,dc=FR est composé des éléments suivants :
  • CN (common name) = jean ⇒ Le nom de l'utilisateur
  • OU (Organisation Unit) = gens ⇒ Le groupe contenant les utilisateurs (ou=machines pour les PC par exemple...)
  • DC (Domain component) dc=EXEMPLE,dc=FR  ⇒ exemple.fr, le domaine du ldap
Le DN d'un utilisateur permet de l'identifier de façon unique dans l'annuaire.

Le principe de l'authentification sur LDAP

Le processus d'authentification sur un LDAP s'appelle un BIND. Il permet de fournir au système LDAP un DN d'utilisateur et un mot de passe (généralement chiffré).
Avant de pouvoir faire un bind, il est nécessaire de récupérer le DN de l'utilisateur. Il n'est pas réaliste de demander à l'utilisateur son DN complet. La première étape est donc généralement une recherche dans l'annuaire sur un attribut (login de l'utilisateur) dans un répertoire de l'annuaire (les utilisateurs généralement).
Une fois le bind réalisé, il est possible de demander des informations supplémentaires à l'annuaire comme par exemple les rôles de l'utilisateur (administrateur, membre d'une liste de diffusion...). Ces éléments pourront être réutilisés dans Spring Security pour automatiser l'attribution d'autorisations.

Exemple d'utilisation

L'exemple suivant montre comment intégrer l'authentification ldap dans un projet comprenant déjà une authentification simple (BDD par exemple) basée sur Spring Security.

L'AuthenticationManager de Spring Security

Ici on configure un authentication-manager grâce à un schéma xml spring security spécialisé. La balise authentication-manager crée automatiquement l'object authentication manager avec une configuration standard efficace. Les enfants de cette balise sont les différentes sources d'authentification utilisée :
  • L'authentification provider ldap (ldapAuthProvider)
  • Un autre authentification provider définissant un sel et un service spécialisé (proxyUserDetailsService2)
<authentication-manager alias="authenticationManager">
   
         <authentication-provider ref="ldapAuthProvider">
          
         <authentication-provider user-service-ref="proxyUserDetailsService2">
               <password-encoder ref="passwordEncoder">
                     <salt-source ref="saltSource">
               </salt-source></password-encoder>
         </authentication-provider>
    </authentication-provider>
</authentication-manager>

Le LDAP AuthenticationProvider

L'objet authenticationProvider est un service SpringSecurity prenant en en entrée un token (par exemple couple login/password) et répondant avec un utilisateur authentifié (ou null s'il n'existe pas). Dans notre cas, l'authentificationProvider à utiliser est créé avec la configuration xml suivante :
<bean class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider" id="ldapAuthProvider">
 <constructor-arg>
  <bean class="org.springframework.security.ldap.authentication.BindAuthenticator">
   <constructor-arg ref="contextSource" />
   <property name="userSearch">
    <bean class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">
     <constructor-arg value="OU=MonGroup">
     <constructor-arg value="(sAMAccountName={0})">
     <constructor-arg ref="contextSource">
    </bean>
   </property>
  </bean>
 </constructor-arg>
 <constructor-arg>
  <bean class="org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator">
   <constructor-arg ref="contextSource">
   <constructor-arg value="OU=Groups,OU=Mycompany,OU=CompanyUsers">
   <property name="rolePrefix" value="LDAP_ROLE_" />
  </bean>
 </constructor-arg>
 <property name="userDetailsContextMapper">
  <bean class="com.company.AcmeContextMapper">
   <property name="AcmeSecurityProviderService" ref="acmeSecurityProviderService" />
  </bean>
 </property>
</bean>
<bean class="org.springframework.security.ldap.DefaultSpringSecurityContextSource" id="contextSource">
 <constructor-arg value="ldap://1.2.3.4:389/dc=exemple,dc=com" />
 <property name="userDn" value="CN=browser,OU=testAccount,DC=exemple,DC=com" />
 <property name="password" value="P@ssw0rd" />
</bean>
Le premier bean définit le service d'authentification. Le premier argument (le bind authenticator) définit les paramètres de la recherche de l'utilisateur dans le LDAP (quel groupe cherche, quelle requête...).
La seconde partie du bean (DefaultLdapAuthoritiesPopulator) définit la recherche des groupes LDAP pour les transformer en rôles Spring security.
La dernière partie (userDetailsContextMapper) est une classe qui permet de convertir l'objet User retournée par le LDAP en un objet User de l'application pour l'enrichir, effectuer des modifications...
Le second bean présente le contexte LDAP avec l'url de connexion et la définition d'un utilisateur permettant de lire les informations du LDAP.

A retenir

La connexion LDAP est une authentification en deux étapes (recherche puis bind) qui permet d'authentifier l'utilisateur et de lui attribuer des rôles. Cette solution permet de mettre en oeuvre rapidement une authentification centralisée.

Les informations utiles pour configurer une connexion LDAP

  • L'URL de connexion au LDAP
  • Un utilisateur ayant les droits pour browser les comptes LDAP :
    • son DN
    • son password
  • L'OU contenant les utilisateurs
  • La requêtes de recherche de l'utilisateur [sur un active directory (sAMAccountName={0}) ]
  • Pour la transformation des groupes en rôles :
    • l'OU contenant les groupes
    • Un préfixe pour les rôles

Article rédigé pour la plateforme de capitalisation technique de Sword

mercredi 15 juin 2011

Hg Init

Un très bon guide pour commencer avec Mercurial : hginit.com

Les chapitres du guide 

vendredi 22 avril 2011

Sécurisation d'une application Flex/Granite DS sur SpringSecurity et Tide

Une application Flex/Java peut utiliser Granite DS/Tide pour simplifier la communication entre les deux tiers de l'application. Spring Security permet la gestion de l'autorisation et de l'authentification. Dans cet article nous allons voir comment utiliser ces deux mécanismes (authentification et autorisation) dans Flex comme si nous étions dans un code Java.

Etablissement du lien entre Flex et Java par Granite DS

La configuration de la communication entre Flex et Java est effectuée grâce à deux fichiers de configuration dans le projet Java :
  • src/main/webapp/WEB-INF/flex/services-config.xml pour la configuration des canaux de communication entre Flex et Java, le lien avec Spring;
  • src/main/webapp/WEB-INF/granite/granite-config.xml pour la configuration de granite et le lien avec Spring Security.
Les configurations (très standard) ont été simplifiées à l'extrême. En effet Granite DS est très bien intégré avec l'écosystème Spring.

Déclaration d'un canal de communication (services-config.xml)

Dans le fichier services-config.xml. Nous allons demander la création d'un service de communication (granite-service) qui s'appuiera sur un canal de communication de type AMF (protocole de communication binaire très efficace d'Adobe) à une adresse précisée dans la configuration :
  • La création du service :
<services>
  <service class="flex.messaging.services.RemotingService" id="granite-service" messagetypes="flex.messaging.messages.RemotingMessage">
   <destination id="spring">
    <channels>
     <channel ref="amf">
    </channel></channels>
    <properties>
     <factory>tideSpringFactory</factory>
     <entitymanagerfactorybeanname>entityManagerFactory</entitymanagerfactorybeanname>
    </properties>
   </destination>
  </service>
 </services>
  • La création du canal :
<channels>
  <channel-definition class="mx.messaging.channels.AMFChannel" id="amf">
   <endpoint class="flex.messaging.endpoints.AMFEndpoint" url="http://{server.name}:{server.port}/{context.root}/amf">
  </endpoint></channel-definition>
 </channels>
Enfin nous allons demander la création d'une factory permettant de créer les objets Java à partir de leurs équivalent Flex. Pour cela nous utilisons un adapteur vers Spring.
<factories>
  <factory class="org.granite.tide.spring.SpringServiceFactory" id="tideSpringFactory">
  </factory>
 </factories>

Configuration de granite (granite-config.xml)

La configuration de granite est encore plus simple. Nous allons juste demander à granite de se configurer avec les annotations dans le code (@RemoteDestination...) et nous lui précisons que la sécurité est gérée par Spring Security ; un adapteur spécifique existe.
<granite-config scan="true">
    <security type="org.granite.spring.security.SpringSecurity3Service">
    </security>
 </granite-config>

Authentification en Flex

Encore une fois le travail est complètement pré-maché par Granite DS. Pour gérer l'utilisateur de l'application Flex, granite met à disposition du programmeur un objet Identity qui permet d'accéder aux informations utiles (données de l'utilisateurs, droits...).
Pour effectuer un login il suffit de faire :
private var __identity:Identity = Identity.instance();
    __identity.login(username, password, onSuccessfulLogin, onFaultLogin);
La méthode effectue exactement la même action que si un utilisateur remplissait un formulaire de login de Spring Security. Les méthodes onSuccessfulLogin et onFaultLogin sont appelée en cas de réussite ou d'échec. Nous pouvons récupérer le nom d'utilisateur avec la méthode suivante :
__identity.username

Autorisation en Flex

Il existe 3 méthodes très utiles sur l'objet Identity permettant de gérer l'autorisation :
  • ifAllGranted : Si l'utilisateur à toutes les autorisations demandées
  • ifAnyGranted : Si l'utilisateur a au moins l'une des autorisations demandées
  • ifNotGranted : Si l'utilisateur n'a pas l'autorisation demandée
Ces méthodes sont asynchrones mais bindable, ce qui permet de les utiliser dans un script mxml :
<mx:button click="updateProduct()" enabled="{dg.selectedItem}" id="bUpdate" label="Update" visible="{identity.hasRole('admin')}"></mx:button>

Ainsi le bouton ne sera visible que pour les administrateurs.
Les appels peuvent aussi être effectués avec un respondeur pour prévoir le prévoir le coté asynchrone de l'appel.
Il existe un cache d'autorisations qui peut être vidé manuellement.

En conclusion

L'utilisation de l'authentification/autorisation est extrêmement simple en Flex, mais parfois un peu limité. Dès qu'un projet à des besoins plus complexes en terme d'autorisation (variabilité en fonction d'un contexte...) il sera nécessaire de s'appuyer sur ces briques de base pour développer un système plus souple.


Article rédigé pour la plateforme de capitalisation technique de Sword

SchemaCrawler pour extraire la structure d'une base de donnée

SchemaCrawler est un framework open-source de découverte de base de données sur jdbc. Il permet de récupérer facilement les métadonnées de la base étudiée. Le framework est aussi publié sous forme d'outil indépendant à utiliser en ligne de commande. Il permet par exemple d'extraire un fichier de description d'une base.
SchemaCrawler permet ainsi de récupérer les informations suivantes :

  • Liste des catalogs (rarement utilisés)
  • Liste des schémas
  • Liste des tables, vues, routines, triggers, contraintes...
  • Liste et détails des champs

Une recherche en deux étapes

La récupération des métadonnées se fait en deux étapes :
  • La définition des options de recherche
  • Le lancement de la recherche sur une connection jdbc

Les options de recherche

La définition des options de recherche est la phase la plus longue et complexe. Des options mal configurées engendrent des temps de réponse très long et une quantité d'informations inutiles et parasites importante. Il est nécessaire de filtrer tout ce que l'on ne souhaite pas récupérer.
Pour filtrer les éléments à conserver, SchemaCrawler met à disposition des objets InclusionRule qui prennent en paramètre une regExp à inclure et une regExp à exclure.
Dans l'exemple suivant, nous définissons un objet option de schemaCrawler permettant de ne récupérer aucune table, aucune vue, aucune procédure, aucun champ... Ainsi nous ne récupérons que la liste des schémas/catalogs de la base.
// Création d'un objet option de SchemaCrawler
    SchemaCrawlerOptions options = new SchemaCrawlerOptions();

    // On n'autorise aucun type de tables (tables, vues...)
    TableType[] types = {};
    options.setTableTypes(types);

    // On exclue toutes les procédures (inclure rien, exclure tout)
    InclusionRule procedureInclusionRule = new InclusionRule("", ".*");
    options.setProcedureInclusionRule(procedureInclusionRule);

    // On exclue toutes les colonnes des procédures (inclure rien, exclure tout)
    InclusionRule procedureColumnInclusionRule = new InclusionRule("", ".*");
    options.setProcedureColumnInclusionRule(procedureColumnInclusionRule);

    // On exclue toutes les tables(inclure rien, exclure tout)
    InclusionRule tableInclusionRule = new InclusionRule("", ".*");
    options.setTableInclusionRule(tableInclusionRule);

    // On fixe le niveau de détail des métadonnées au minimum
    options.setSchemaInfoLevel(SchemaInfoLevel.minimum());

    // On exclue les procédure stockées
    options.setShowStoredProcedures(false);

Le lancement de la recherche

Le lancement de la recherche est la partie la plus simple. Il suffit de faire appel à la méthode SchemaCrawlerUtility.getDatabase et de passer en paramètre une connexion à la base de données valide (de type java.sql.Connection) et un objet option de schemaCrawler.
Database crawlingDatabase = SchemaCrawlerUtility.getDatabase(databaseConnection, options);
Une fois l'objet database de SchemaCrawler récupéré, il est très simple de parcourir les métadonnées. Par exemple pour tracer la liste des schémas de la base de données :
for (final Schema schema : crawlingDatabase.getSchemas()) {
        log.info("Found database schema " + schema.getSchemaName() + " in catalog " + schema.getCatalogName());
    }

Différentes options intéressantes

Comme expliqué dans le chapitre précédent, la complexité d'utilisation de ce framework réside principalement dans la configuration de l'objet option permettant de spécifier correctement les informations à remonter pour réduire au maximum les temps de recherche en base (un scan complet de la base étant extrêmement long). Dans cette section, nous allons proposer deux exemples types de définition de l'objet option pour récupérer les schémas puis les tables et vues. Sur le même principe toutes les métadonnées de la base peuvent être récupérées.

Récupération de la liste des schémas

Idem chapitre précédant :
// Création d'un objet option de SchemaCrawler
    SchemaCrawlerOptions options = new SchemaCrawlerOptions();

    // On n'autorise aucun type de tables (tables, vues...)
    TableType[] types = {};
    options.setTableTypes(types);

    // On exclue toutes les procédures (inclure rien, exclure tout)
    InclusionRule procedureInclusionRule = new InclusionRule("", ".*");
    options.setProcedureInclusionRule(procedureInclusionRule);

    // On exclue toutes les colonnes des procédures (inclure rien, exclure tout)
    InclusionRule procedureColumnInclusionRule = new InclusionRule("", ".*");
    options.setProcedureColumnInclusionRule(procedureColumnInclusionRule);

    // On exclue toutes les tables(inclure rien, exclure tout)
    InclusionRule tableInclusionRule = new InclusionRule("", ".*");
    options.setTableInclusionRule(tableInclusionRule);

    // On fixe le niveau de détail des métadonnées au minimum
    options.setSchemaInfoLevel(SchemaInfoLevel.minimum());

    // On exclue les procédure stockées
    options.setShowStoredProcedures(false);

Récupération de la liste des tables et vues

// Création d'un objet option de SchemaCrawler
    SchemaCrawlerOptions options = new SchemaCrawlerOptions();

    // On autorise les tables et les vues
    TableType[] types = { TableType.table, TableType.view };
    options.setTableTypes(types);

    // On exclue toutes les procédures (inclure rien, exclure tout)
    InclusionRule procedureInclusionRule = new InclusionRule("", ".*");
    options.setProcedureInclusionRule(procedureInclusionRule);

    // On exclue toutes les colonnes des procédures (inclure rien, exclure tout)
    InclusionRule procedureColumnInclusionRule = new InclusionRule("", ".*");
    options.setProcedureColumnInclusionRule(procedureColumnInclusionRule);

    // On exclue les procédure stockées
    options.setShowStoredProcedures(false);

    // On inclue toutes les tables mais on exclue les tables commençant par $ (pour éviter les tables système d'Oracle)
    InclusionRule tableInclusionRule = new InclusionRule(".*", ".*[\\x24]+.*");
    options.setTableInclusionRule(tableInclusionRule);

    // On fixe le niveau de détail des métadonnées au minimum
    options.setSchemaInfoLevel(SchemaInfoLevel.minimum());

    // On spécifie le schéma que l'on souhaite crawler
    InclusionRule schemaInclusionRule = new InclusionRule("MySchema", "");
    options.setSchemaInclusionRule(schemaInclusionRule);
Article rédigé pour la plateforme de capitalisation technique de Sword

jeudi 10 mars 2011

Personal Password Policy

Ça fait bien longtemps que je n'ai pas posté ici. Il faut croire que ce n'est plus vraiment ma priorité, mon boulot et ma vie personnelle proposant déjà plein de priorités. Néanmoins, j'ai plein de projets d'articles dans mes tiroirs (virtuels) et je vais commencer par un sujet que je viens d'appliquer : la gestion des password.


Je ne vais pas faire dans cet article la liste de tous les outils qui se souviennent pour vous de vos passwords ; je n'aime pas ça, ça donne une impression de sécurité, mais si le logiciel à une faille ou si le password maître est divulgué tout le système s'écroule et vous n'avez plus aucune sécurité. De plus, il apporte son lot de complexité à partir du moment où l'on souhaite avoir une synchronisation entre différentes machines avec une sauvegarde en ligne...

Organisation hiérarchique des passwords
La réflexion que je vais partager ici porte sur mon organisation hiérarchique des passwords. Les théories extrémistes voudraient que l'on utilise un password par service, avec une complexité importante dans la génération du password (du style 4Hx-v8[A$) et sans réutilisation. Ceux qui me connaissent savent que je ne pourrais jamais appliquer cette théorie à cause de ma mémoire de poisson rouge. La parade que j'ai trouvée est de segmenter les services par niveau de sécurité des données présentes. Chaque niveau utilise ainsi un même login/password. Je distingue ainsi quatre niveaux :
1_ Les services bancaires
Il s'agit ici de mon compte bancaire en ligne. Il est le seul à avoir un login et un password unique qui n'est réutilisé sur aucun service.
En cas de compromission : risque de vol sur mon compte en banque limité par la sécurité renforcée de ma banque (authentification forte mise en place par ma banque).

2_ Les services centraux de confiance
J'inclue dans ces services mon compte Google ainsi que mon compte dans l'Active Directory de l'entreprise. Les deux sont des services de confiance. Ces comptes peuvent théoriquement compromettre les comptes des niveaux inférieurs. En effet, de trop nombreux services envoient un récapitulatif d'inscription avec login/password. J'ai aussi des password enregistré dans mon navigateur, ce qui est une possible faille.
En cas de compromission : inquiétante, mais peu probable. Elle mettrait en danger tous les comptes de niveau 2 et 3. Elle nécessiterait une modification urgente de nombreux passwords.

3_ Les services périphériques
La majorité des services en ligne rentrent dans cette catégorie, j'ai suffisamment confiance en eux pour leur communiquer une identité réutilisée partout sur Internet. Ils sont suffisamment connus pour ne pas pratiquer de vols d'identité. Par contre, je n'ai pas suffisamment confiance dans leur politique de sécurité. Ils sont vulnérables à un vol massif de leur base de données utilisateurs (comme cela s'est déjà vu sur de nombreux sites).
En cas de compromission : modification de mon login/password sur tous les services de ce niveau pour éviter un possible vol d'identité.

4_ Les services anonymes
J'inclue dans cette catégorie tous les sites douteux ou je préfère rester anonymes (torrents, direct download, compte temporaires pour télécharger une application...). J'utilise généralement une adresse mail jetable (cf. yopmail.com) et un nom d'utilisateur différent. Aucun élément de cette identité n'est identique à mon identité réelle (nom, prénom, date de naissance...).
En cas de compromission : aucune perte de sécurité sur les niveaux supérieurs

Création des passwords
La modification d'un password sur de nombreux service n'étant pas aisée. Je réserve la modification régulière de mon password aux services de niveau 1 et 2. Les deux services de niveaux 2 sont basés sur des annuaires centraux. Ainsi la modification de mon password dans ces deux interfaces me permet d'accéder à de nombreux services (de mon entreprise et de google) avec un mot de passe changé régulièrement. Pour générer un nouveau password, il existe plusieurs possibilités :
  • L'incrémentation : votre password de base est toto, vos mots de passe suivants sont toto1, toto2... -> Très mauvaise sécurité ;
  • La modification : vous déformez le password initial : toto -> titi -> tata... -> sécurité moyenne ;
  • La génération aléatoire : vous utilisez un générateur de password aléatoire sur internet et l'apprenez par-coeur -> Bonne sécurité ;
  • Mnémotechnique : Prenez une phrase que vous aimez, garder la secrète et construisez votre password en ne conservant que la première lettre/syllabe. Ainsi "Je suis beau et fort" devient jsbef ou Jesube&fo -> Bonne sécurité.
Les questions secrètes
Cette étape de l'inscription à un service est souvent la faille majeure du système. Pour ceux qui ne connaissent pas, l'utilisateur doit choisir une question est donner sa réponse (par exemple "votre couleur préférée" et "bleu"). Si l'utilisateur oublie son password, le site lui posera la question qu'il aura configurée et comparera sa réponse à la réponse enregistrée. Si les réponses concordent, un mail lui est envoyé avec un nouveau password.
Cette technologie est la cible de social engineering qui permet grâce à l'étude de l'environnement social de l'utilisateur de répondre correctement à la question enregistrée (généralement relative à la vie privée de l'utilisateur). Combinée à une compromission du compte de messagerie de l'utilisateur, il permet généralement de faire de gros dégâts.
Pour limiter les risques, il faut faire attention à ne pas utiliser de question secrète ou a utiliser une question secrète peu sensible au social engineering. Évitez donc toute donnée qui pourrait être publique ou connue d'autres personnes (date de naissance de vos parents, votre institutrice de maternel...)

Dans la mesure du possible utilisez votre propre question secrète la plus ambiguë possible et la moins sensible à une recherche google.

vendredi 3 juillet 2009

Contrat de travail signé !

C'est enfin fait, une semaine après la fin de mon stage, je signe mon nouveau contrat de travail, un CDI chez Sword !


PS : Merci Sanh pour le contact !