Git pour les imbéciles comme moi

Git, je n’y comprenais rien. Et puis j’ai vu la présentation de Sébastien Douche (twitter, blog) au Paris JUG et les choses alors sont devenues beaucoup plus claires. Cette présentation doit absolument être vue par tous les développeurs qui utilisent un logiciel de gestion de source (donc tous les développeurs). Ce qui me désole le plus c’est qu’avant j’étais heureux dans l’ignorance ; désormais je souffre quand je dois utiliser subversion…

Les concepts

La pratique

Posted in Tests, build, qualité, etc | Tagged , , , | Leave a comment

Java 7 : les nouveautés côté sécurité

La sortie de Java 7 est imminente et on a déjà beaucoup parlé des grandes nouveautés de cette version (multi-catch, opérateur diamant, Fork/Join, opcode invokedynamic…). Mais Java 7 arrive également des tas de petites améliorations, nouveautés et corrections de bugs. Voilà donc un petit résumé des principales nouveautés de Java 7, côté sécurité.

Cryptographie sur Courbes Elliptiques

Java 7 est désormais livré avec le provider SunEC (sun.security.ec.SunEC) dédie à la cryptographie sur courbes elliptiques et fournissant un support natif à travers la bibliothèque sunecc. Jusqu’à présent la cryptographie sur courbes elliptique n’était disponible que par le provider SunPKCS11 sous réserve de posséder un dispositif supportant ces algorithmes (carte à puce…) ou en utilisant le provider Bouncy Castle.

Par exemple, obtenir un objet Signature pour l’algorithme ECDSA se fait de la façon suivante :

Signature sg = Signature.getInstance("SHA1withECDSA");
System.out.println(sg.getProvider()); // affiche "SunEC version 1.7"

SSL

Attaque contre les algorithmes en mode CBC

L’implémentation TLS 1.1 a été mise à jour pour se protéger contre l’attaque décrite dans ce document.

Contrôle de la session TLS

De nouvelles classes et méthodes donnent un contrôle plus fin sur la connection TLS :

  • X509ExtendedTrustManager (une implémentation de TrsutManager) permet d’accéder aux paramètres de la connection TLS pendant le handshake.
  • SSLParameters.setEndpointIdentificationAlgorithm permet de définir un algorithme de vérification de l’identité du serveur lors du handshake. Dans les versions précédentes, cette vérification ne se faisait que pour le protocole HTTPS et passait par l’interface HostnameVerifier. Désormais on peut l’effectuer au niveau de la couche TLS ; la bibliothèque standard fournit deux algorithmes de vérification : HTTPS et LDAPS. Malheureusement il ne semble pas possible d’ajouter d’autres algorithmes de vérification.
  • L’utilisation des algorithmes rendus obsolètes (et déconseillés) dans les RFC 4346, RFC 5246 et RFC 5469 est désormais désactivée par défaut.

Support de SNI

SNI (Server Name Indication) est une extension ajoutée au protocole TLS et définie dans la RFC 6066. Cette extension contient le nom du serveur auquel le client veut accéder dans le message ClientHello. De cette façon le serveur sait immédiatement quel nom a été demandé et peut donc choisir quel certificat envoyer au client. Cela permet de configurer des VirtuatHosts en HTTPS comme on le fait en HTTP. Auparavant chaque serveur HTTPS devait posséder sa propre adresse IP.

Il est facile de vérifier si l’extension SNI est supportée en envoyant une requête à l’URL https://sni.velox.ch :

public static void main(String[] args) throws Exception {
    URL url = new URL("https://alice.sni.velox.ch");
    HttpsURLConnection con = (HttpsURLConnection)url.openConnection();
    BufferedReader reader = new BufferedReader(new InputStreamReader(con.getInputStream()));
    FileWriter writer = new FileWriter("index.html");
    while (true) {
        String line = reader.readLine();
        if (line == null) break;
        writer.append(line);
    }
    writer.close();
}

Ouvrez ensuite le fichier index.html dans votre navigateur. En change l’URL de la requête en bob.sni.velox.ch ; avec Java 7 cela fonctionne correctement mais Java 6 lève une exception :

Exception in thread "main" javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No subject alternative DNS name matching bob.sni.velox.ch found.

En effet, bob.sni.velox.ch est un hôte virtuel, si l’extension SNI n’est pas envoyée alors le serveur utilise l’hôte par défaut qui est alice.sni.velox.ch, ce qui explique l’erreur obtenue.

Des questions, des remarques, des éclaircissements ? N’hésitez pas poster un commentaire.

Posted in Du code, rien que du code | Tagged , , , , , , | Leave a comment

Security Stack Exchange

Security Stack Exchange LogoLe site de questions/réponses Security Stack Exchange vient de sortir de sa phase de béta test et devient donc officiellement le 28e site du réseau Stack Exchange (dont la tête de pont est le célèbre Stack Overflow, le site de questions/réponses dédié aux programmeurs de tout poil).

Nouveau design donc pour le site qui abandonne les couleurs de la béta, mais également ouverture d’un blog et d’un compte twitter. Si vous avez des questions sur la sécurité des applications web, sur la certification des PKI, sur les cartes à puce, sur les standards cryptographiques… vous êtes au bon endroit.

À noter également l’ouverture (mais pour le moment en béta privée) d’un autre site dédié spécifiquement aux questions touchant la cryptographie.

Posted in Ici et maintenant | Tagged , | Leave a comment

Code comparé : SHA-1 sur Android & iOS

Problème : on dispose d’un bloc de données dont on désire calculer l’empreinte SHA-1. Deux solutions, sur Android et iOS.

Android

import java.security.MessageDigest;
/* ... */
public static byte[] computeSHA1(byte[] input) {
    MessageDigest dg = MessageDigest.getInstance("SHA-1");
    return dg.digest(input);
}

iOS

#import <CommonCrypto/CommonDigest.h>
/* ... */
+ (NSData*) computeSHA1:(NSData*)input
{
    unsigned char output[CC_SHA1_DIGEST_LENGTH];
    CC_SHA1([input bytes], (CC_LONG)[input length], output);
    return [NSData dataWithBytes:output length:CC_SHA1_DIGEST_LENGTH]}

Une question, une remarque, une erreur (je découvre Objective-C), n’hésitez pas à poster un commentaire.

Posted in Android, iPhone, mobilité, Du code, rien que du code | Tagged , , , , | Leave a comment

SSL/TLS en Java – Partie 1

J’ai pensé appeler ce post «Pourquoi SSL ne marche pas ?». En effet j’ai lu beaucoup de questions sur des forums posées par des développeurs qui n’arrivaient pas à faire fonctionner SSL entre le serveur et les clients ; le plus souvent le développeur ne sait pas très bien comment fonctionne le protocole et il n’arrive pas à diagnostiquer les problèmes ; j’espère que cette série article aidera ceux qui se battent encore avec les erreurs du type alert=42 (si si, je vous assure que c’est une vraie erreur :) ).

SSL en quelques mot

D’abord un peu de vocabulaire, on lis souvent SSL, TLS ou encore SSL/TLS. Pour faire court, tout ça c’est la même chose. Pour entrer un peu dans les détails, SSL a été inventé par Netscape qui publia SSL 2.0 en 1995 puis SSL 3.0 en 1996. Puis ce fut le TLS Working Group membre de l’IETF qui repris le standard en publiant TLS 1.0 (aussi appelé SSL 3.1) en 1999, TLS 1.1 en 2006 et TLS 1.2 en 2008. Pour la petite histoire, SSL apparait toujours dans TLS car la structure de données contenant le numéro de version du protocole vaut 3.1 pour TLS 1.0, 3.2 pour TLS 1.1 et 3.3 pour TLS 1.2.

Mais finalement à quoi ça sert exactement ? SSL fournit un canal sécurisé entre un client et un serveur en fournissant les services suivants :

  • chiffrement des messages
  • intégrité des message (utilisation d’un Message Authentication Code)
  • authentification du serveur (basée sur des certificats X.509)
  • authentification du client (optionnelle)

À noter que, stricto sensu, le serveur n’est pas obligé de s’authentifier auprès du client et que l’usage de certificats X.509 n’est pas obligatoire. Cependant il s’agit de cas d’utilisation marginaux.

Un peu de Java

Le support de SSL en Java a d’abord été fournit sous forme d’une extension la Java Secure Socket Extension ou JSSE ; à partir de Java 1.4, la JSSE a été incluse dans la bibliothèque standard. L’API de la JSSE est couverte par les packages java suivants : javax.net, javax.net.ssl et javax.security.cert.

Alors, comment s’en sert on ? L’API JSSE a introduit la classe javax.net.ssl.SSLSocket, une sous-classe de java.net.Socket qui encapsule toute la complexité du protocole SSL. On ne peut pas instancier directement de SSLSocket car son constructeur est protected, il faut aller passer par une autre class de la JSSE javax.net.ssl.SSLSocketFactory.

SocketFactory sf = SSLContext.getDefault().getSocketFactory();
Socket socket = sf.createSocket("www.example.com", 443);

Sur le web on va plutôt utiliser le protocole HTTP/S ; la JSSE fournit le handler javax.net.ssl.HttpsURLConnection qui permet de gérer ces URL en https://. L’utilisation devient alors complètement transparente :

URL url = new URL("https://www.example.com");
URLConnection conn = url.openConnection();
System.out.println(conn instanceof HttpsURLConnection); // affiche "true"

Voilà un schéma tiré de la documentation officielle d’Oracle qui montre les relations entre les classes de la JSSE.

JSSE Classes

La classe centrale est SSLContext et c’est par cette classe que passe la configuration de SSL. Le code va alors ressembler à ça :

SSLContext ctx = SSLContext.getInstance("TLS");
/* ici on configure le contexte SSL */
// ctx.init(... parameters ...);
SSLSocketFactory sf = ctx.getSSLSocketFactory();
/* on configure la socket factory utilisée pour les connexions aux URLs en https:// */
HttpsURLConnection.setDefaultSSLSocketFactory(sf);

Question de confiance

Avec SSL tout est une question de confiance. Lors du hanshake le serveur envoie son certificat (pour être plus précis, une chaîne de certificat qui contient également toutes les autorités de certification intermédiaires) au client qui doit le valider. La validation de ce certificat implique de relier la chaîne de certificats du serveur à une racine de confiance (ou trust anchor).

La bibliothèque standard Java est livrée avec une liste de d’autorités racines qui se trouve un fichier de type KeyStore. Sur ma machine (un Mac) le fichier se trouve là :
/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home/lib/security/cacerts

On peut obtenir la liste de ces autorités avec l’outil keytool

keytool -list -keystore cacerts

Ici le mot de passe demandé ne sert qu’à vérifier l’intégrité du fichier, il n’y a pas de clés dont il faut protéger la confidentialité ; et par défaut le mot de passe est changeit.

Cependant il peut arriver que le serveur contacté n’ait pas de certificat signé par une autorité reconnue (test, application sur un intranet, pas d’argent pour s’acheter un certificat…). Arrive alors inéluctablement cette erreur :

Exception in thread "main" javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

Que s’est il passé ? Le message de l’erreur nous indique qu’il a été impossible de trouver un chemin de certificats valide pour le certificat du serveur.

Le mécanisme de validation d’une chaîne de certificat est accessible via un TrustManager, et la classe TrustManagerFactory permet d’en créer une instance.Pour régler notre problème il faut donc un TrustManager qui prenne en compte notre nouvelle autorité racine.

Première solution, ajouter l’autorité au fichier cacerts. Dans ce cas l’autorité racine pourra être utilisée par le TrustManager par défaut. Pas terrible pour faire des tests mais c’est une solution envisageable quand on a une PKI interne.

Seconde solution : utiliser un TrustManager dédié qui fait confiance à notre nouvelle autorité. D’abord il faut créer un KeyStore contenant notre racine. Cela se fait simplement avec keytool :

keytool -importcert -trustcacerts -noprompt -alias myTrustAnchor -file /path/to/my/root.crt -keystore /path/to/my/keystore

L’option -trustcacerts permet de créer une entrée de type TrustedCertificateEntry dans le key store.

Ensuite, il existe deux options. Soit on demande au TrustManager par défaut d’utiliser notre key store ; dans ce cas on le déclare en passant une variable d’environnement à la JVM :

java -Djavax.net.ssl.trustStore=/path/to/my/keystore -jar ...

Soit on instancie un TrustManager configuré avec notre key store de façon programmatique :

/* instancier le KeyStore */
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(new FileInputStream("/path/to/my/keystore"), null);
/* initialiser une TrustManagerFactory avec ce KeyStore */
TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX");
tmf.init(ks);
/* créer un SSLContext utilisant cette TrustManagerFactory */
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, tmf.getTrustManagers(), null);

Prenez le contrôle

Jusqu’à présent on a pu configurer la source des racines de confiance ; on peut donc utiliser des certificats émis par n’importe qui. Mais alors que vous venez de finir cette configuration voilà une nouvelle erreur :

Exception in thread "main" javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path validation failed: java.security.cert.CertPathValidatorException: revocation statut check failed: no CRL found

Et oui, le certificat auto-signé que vous aviez bricolé sur le coin d’une console ne contient pas les informations permettant de le valider, impossible donc de déterminer si ce certificat a été révoqué ou non. Ici le problème est que le comportement par défaut du TrustManager et de rejeter tout certificat dont le statut de révocation n’a pu être établi.

Pour passer outre ce comportement et pouvoir utiliser notre certificat de test il faudrait pouvoir désactiver cette vérification du statut de révocation du certificat dans le TrustManager. L’API permet une configuration plus fine des paramètres de validation, cela passe par la classe PKIXBuilderParameters.

PKIXBuilderParameters permet de configurer le comportement de la validation de chaînes de certificats en lui passant un certain nombre de paramètres parmi lesquels :

  • la liste des racines (sous forme d’un KeyStore ou d’un Set de TrustAnchor)
  • la date à laquelle on fait la validation (par défaut, maintenant)
  • si on vérifie le statut de révocation des certificats
  • des magasins de certificats et de listes de révocation supplémentaires au cas où (par exemple un serveur LDAP ou seraient publiées des listes de révocation)
  • et d’autres…

Comment régler notre problème de vérification du statut de révocation ? En construisant un PKIXBuilderParameters qui ne fait pas cette validation :

KeyStore st = KeyStore.getInstance("JKS");
/* ... */
PKIXBuilderParameters params = new PKIXBuilderParameters(ks, null);
/* désactivation la révocation */
params.setRevocationEnabled(false);
TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX");
tmf.init(new CertPathTrustManagerParameters(params));
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, tmf.getTrustManagers(), null);

Dans cet exemple la vérification du statut de révocation a été désactivée ; c’est acceptable dans le cas d’un test mais fortement déconseillé dans un contexte de production. Dans le cas courant l’algorithme de validation va chercher dans le certificat l’URL où il peut télécharger la liste de révocation de l’autorité qui a émis ce certificat. La liste de révocation (CRL pour Certificate Revocation List en anglais) contient, pour simplifier, la liste des numéros de série des certificats qui n’ont pas encore expirés mais qui ont été révoqués par leur porteur. Pour certaines autorités émettant beaucoup de certificats ces CRL peuvent atteindre plusieurs Mo, voire plusieurs dizaines de Mo.

Pour réduire la consommation de bande passante, le standard OCSP (Online Certificate Status Protocol, spécifié dans la RFC 2560) a été inventé. Il s’agit d’un protocole qui permet de demander à une serveur (habituellement appelé répondeur OCSP) de façon interactive le statut de révocation d’un unique certificat. La réponse peut être « valide », « invalide » ou « inconnu » (comprendre que le serveur n’a pas l’information pour répondre).

Par défaut OCSP n’est pas utilisé pour la validation. Pour l’activer il faut soit modifier le fichier java.security, soit l’activer de façon programmatique dans le code :

Security.setProperty("ocsp.enabled", "true");

Si OCSP est activé alors l’algorithme de validation essaie d’abord de l’utiliser et repasse sur les CRL en cas d’échec, par exemple si le certificat n’indique pas de répondeur OCSP à contacter. Il est également possible de préciser l’une URL d’un répondeur OCSP à toujours contacter ; cela peut être utile dans le cas d’un répondeur OCSP installé en proxy dans une entreprise.

Security.setProperty("ocsp.responderURL", "http://ocsp.example.net");

Conclusion temporaire

J’ai fait le tour, un peu succinctement j’avoue, sur la validation du certificat du serveur. Mais il reste beaucoup à dire sur SSL : authentification du client, les ciphers suites, d’autres APIs… Ce sera l’objet de la seconde partie.

Posted in Du code, rien que du code | Tagged , , , , | Leave a comment

Audit automatique avec Play Framework

J’aime beaucoup l’annotation @With de Play framework, elle permet d’étendre le comportement d’un Controller en y ajoutant simplement des intercepteurs. Par exemple il est possible d’écrire un Controller générique dont le travail est de logguer des appels et d’ajouter ce comportement sur d’autre Controller avec une simple annotation.

On commence par définir une annotation qui servira à taguer les méthodes dont les appels seront loggués:

package controllers;
 
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
 
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Audit { }

Ensuite on va rechercher toutes les méthodes taguées avec cette annotation et on ajoute une ligne de log contenant des informations pertinentes.

package controllers;
 
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import play.Logger;
import play.mvc.ActionInvoker;
import play.mvc.Finally;
import play.mvc.Controller;
import play.mvc.Http;
import play.utils.Java;
 
public class AuditTrail extends Controller
{
    @Finally
    static void log() throws Throwable {
        if (getActionAnnotation(Audit.class) != null) {
            audit();
        }
    }
 
    private static void audit() throws Throwable {
        /* Get the method name */
        Object[] action = ActionInvoker.getActionMethod(Http.Request.current().action);
        Class c = (Class)action[0];
        Method m = (Method)action[1];
        /* Get the method parameters name... */
        String[] names = Java.parameterNames(m);
        /* ... and the values */
        Object[] args = ActionInvoker.getActionMethodArgs(m, null);
        List<String> params = new ArrayList<String>();
            for (int i = 0; i < names.length; i++) {
                params.add(names[i] + "=" + args[i]);
        }
        /* Get the username if Secure module is used */
        String username = session.get("username");
        if (username == null) {
            username = "Anonymous User"
        }
        /* Log the action */
        Logger.info("<%s> called contoller action <%s.%s> with parameters <%s> at <%s>",
            username, c.getSimpleName(), m.getName(), params, new Date());
    }
}

Enfin, pour utiliser notre Audit Trail, rien de plus simple, il suffit d’ajouter un @With(AuditTrail.class) sur un Controllers et d’annoter avec @Audit les méthodes à auditer.

@With({Secure.class, AuditTrail.class})
public class Issues extends Controller
{
    @Audit
    public static void showTicket(long ticketId) {
        /* Do some stuff */
        render(...);
    }
}

Et dans les logs on pourra alors trouver une ligne qui ressemble à ça :

17:45:53,867 INFO  ~ <johndoe> called contoller action <Issues.showTicket> with parameters <[ticketId: 11484]> at <Mon May 02 17:45:53 CEST 2011>

Beaucoup d’améliorations sont possibles comme par exemple indiquer le Logger à utiliser comme paramètre de l’annotation Audit ou encore mettre les logs dans la base de donnée pour faire des recherches.

Posted in Du code, rien que du code | Tagged , , , , | Leave a comment

Analyse de la qualité logicielle

Une sympathique session du Lausanne JUG concernant l’analyse de la qualité des logiciels. Le format de la présentation, en forme de « confrontation » (mais pleine de fair-play) me plait assez : cinq éditeurs de logiciels présentent cinq outils (en fait quatre seulement dans la vidéo, le dernier n’ayant semble-t-il pas souhaité être filmé), chacun faisant en 20 minutes la démonstration de ce qu’il peut/sait faire en analysant le même logiciel. En l’occurrence c’est IceScrum 2 qui a servi d’étalon.

Les outils présentés :

A lire également, sur le blog d’OCTO Technology, les commentaires et impressions de Cyril Picat qui a organisé l’événement.

Et si jamais quelqu’un du Paris JUG me lit, pourquoi ne pas organiser une session de ce genre ?

Posted in Tests, build, qualité, etc | Tagged , , , | Leave a comment

Parser des parties d’échecs avec Scala Parser Combinators

Un morceau de code scala écrit il y a quelques mois quand j’ai commencé à jouer avec scala. Il utilise l’API parser combinators de scala pour décoder des parties d’échecs au format PGN.

Posted in Du code, rien que du code | Tagged , , | Leave a comment

Afficher les vidéos de Parleys dans Confluence

Comment faire quand on désire afficher du contenu multimedia (une vidéo par exemple) dans une page Confluence ?
Confluence fournit une la balise wiki dédiée si la ressource à afficher est stockée localement (un attachement dans le wiki par exemple). Une autre solution c’est le plugin widget connector qui est compatible avec des services comme youtube, vimeo, slideshare… Malheureusement aucune de ces solutions ne permet d’inclure une vidéo issue de Parleys.

Continue reading

Posted in Du code, rien que du code | Tagged , , , , | Leave a comment

Sommes-nous des professionnels ?

Les vidéos de la conférence Devoxx 2009 sont désormais toutes librement accessibles ; j’en profite donc pour inciter toute personne travaillant dans le développement logiciel à regarder la keynote de Robert Martin (alias oncle Bob) sur ce que devrait être, et devrait faire, un professionnel du développement logiciel. À Lire également, l’article de wikipedia Software craftsmanship.

Alors certes, rien de bien nouveau dans le discours ; mais il est toujours bon de rappeler les évidences, ne serait-ce que pour nous montrer qu’il nous reste une marge de progression…

Posted in Du code, rien que du code, Tests, build, qualité, etc | Tagged , , , , | Leave a comment