mercredi 7 février 2018

10 hacks en Java

Parfois, il n'est pas possible de faire certaines choses aisément en Java, parce que le langage et son environnement ne sont pas conçus pour ça. Grosse frustration.

Par exemple, étendre un Enum n'a pas de sens, si on éprouve l'idée sur la notion d'extension telle qu'elle est convenue ; pourtant, il peut arriver qu'on en ait besoin si on considère la notion d'extension différemment. Bien que la JVM ne le permette pas de manière triviale, quelques circonvolutions nous permettent cependant d'obtenir l'effet souhaité. On va décrire comment tout à l'heure.

J'expose ici 10 recettes permettant d'étendre certaines fonctionnalités à priori pas permises en Java : 10 hacks bien commodes dont je me suis déjà servi, et qu'un jour peut-être tu trouveras utile pour ton propre code, ami lecteur.

Une alimentation saine et équilibrée
permet de bien faire hack hack

Au menu :
  1. Ecrire dans un champ static final
  2. Propager une exception sans la wrapper
  3. Ecrire une expression lambda sans traiter les exceptions
  4. Connaître la classe appelante d'une méthode
  5. Renommer un Enum
  6. Réordonner un Enum
  7. Etendre un Enum
  8. Obtenir dynamiquement une instance d'une interface
  9. Retourner un objet adapté au type du champ qui le recevra
  10. Protéger les mots de passe dans un serveur Web
A l'heure ou j'écris ces lignes, le code des outils mentionnés ci-après sont disponibles dans Github, mais pas encore disponible dans le Maven repo : vous devrez faire un checkout des différents projets et construire les archives vous-mêmes en attendant.

Ecrire dans un champ static final



Problème : on dispose d'un membre static final, et on souhaite changer sa valeur. Typiquement, dans une interface :
interface SomeInterface {

    static Whatever whatever = new Whatever();

}
Si vous êtes tentés d'assigner une nouvelle valeur, votre code ne pourra pas compiler :
    SomeInterface.whatever = new Other(); // Other étend Whatever

Pour affecter une nouvelle valeur, il nous faut le champs correspondant, et lui assigner la nouvelle valeur :
    Field whatever = SomeInterface.class.getField("whatever");
    whatever.setAccessible(true);
    whatever.set(SomeInterface.class, new Other());

Le code ci-dessus cause cependant une erreur :
Exception in thread "main" java.lang.IllegalAccessException:
Can not set static final hack.SomeInterface$Whatever field
hack.SomeInterface.whatever to hack.SomeInterface$Other

Pas besoin de marquer whatever final dans l'interface, c'est une caractéristique inhérente à ce champ. Et puisqu'elle n'y est pas, on ne peut pas l'enlever. Sauf par programmation, il est possible de la désactiver avant de mettre à jour la valeur ; le code final est celui-ci :
    Field whatever = SomeInterface.class.getField("whatever");

    Field modifField = Field.class.getDeclaredField("modifiers");
    modifField.setAccessible(true);
    final int modifiers = whatever.getModifiers();
    modifField.setInt(whatever, modifiers & ~Modifier.FINAL);
    whatever.setAccessible(true);
    whatever.set(SomeInterface.class, new Other());
    modifField.setInt(whatever, modifiers);


Propager une exception sans la wrapper



La gestion des exceptions a toujours été un problème délicat en Java. C'est parfois plus gênant que commode : la plupart du temps, il n'y a pas lieu de traiter l'exception là où Java nous oblige à la traiter, alors il suffit de la propager pour qu'elle soit traitée dans une sorte de "point de ralliement" de l'application (ServletFilter, AOP, etc) plutôt que ne soit éparpillé le code susceptible de la traiter. Une bonne pratique consiste donc à traiter les exceptions le plus tard possible, mais dans tous les cas, ne jamais perdre leur trace ! J'ai très souvent vu du code comme ça :
    try {
        doWhatever();
    } catch (WhateverException e) {
        throw new ItIsInTheSignatureException("An error occurs");
    }

Parfois, la cause d'origine est écrite dans les logs, ou écrite dans le sysout, ou tout simplement perdue comme le montre le code ci-dessus. Toutes ces variantes font partie de la pire des pratiques : à moins de savoir comment corriger le problème, il faut toujours en propager la cause.

Concernant la propagation, on peut difficilement se contenter d'ajouter toutes les exceptions à la liste des exceptions que notre méthode doit déclarer, et ainsi de suite en remontant dans la pile d'appel ; on se trouverait avec des dizaines d'exception ce qui n'apporte rien d'utile.

La parade est bien sûr d'appliquer ce qui est conseillé quand on apprend le langage Java : créer une exception spécifique au problème que l'on traite. Ce qui est embêtant c'est que cette pratique a tendance à démultiplier le nombre de classes, alors que parfois on a juste besoin d'un wrapper pour une bête IOException.

Parfois on est simplement bloqué parce qu'il n'y a pas d'exception déclarée dans la signature d'une méthode qu'on implémente. La plus commode des solutions est de recourir à un wrapper neutre, qui engendre une exception qui ne nécessite pas d'être traitée sur place :
    try {
        doWhatever();
    } catch (WhateverException e) {
        throw new RuntimeException("An error occurs", e);
    }

Bien sûr, il ne faudra pas oublier d'attraper ce type d'exceptions au "point de ralliement" de l'application. Le problème est qu'il faille les déshabiller. On peut faire mieux :
    try {
        doWhatever();
    } catch (WhateverException e) {
        Thrower.doThrow(e); // e is not wrapped
    }

La classe Thrower permet de propager l'exception d'origine sans wrapper. C'est une sorte de "cast" de cette exception en RuntimeException bien qu'elle ne soit pas nécessairement compatible. Voici son code :
public class Thrower {

    public static <T> T doThrow(Exception e) {
        Thrower.<RuntimeException> throwAs(e);
        return null;
    }

    @SuppressWarnings("unchecked")
    private static <E extends Exception> void throwAs(Exception e) throws E {
        throw (E) e;
    }

}

On remarque que null est retourné dans le type d'appel, ce qui permet si le code nous y oblige de l'utiliser sous cette forme :
    try {
        return doWhatever();
    } catch (WhateverException e) {
        return Thrower.doThrow(e); // e is not wrapped
    }


Ecrire une expression lambda sans traiter les exceptions



Comme dit précédemment, et pour insister un peu, la gestion des exceptions est pénible. Ce n'est plus une bonne pratique que d'obliger les usagers de votre code à gérer à tour de bras des exceptions. Ne nous y trompons pas, les nouvelles API depuis Java 8 sont assez sympa avec nous quand il y a des erreurs à traiter. Par exemple vous pouvez écrire simplement :
    LocalDate ld = LocalDate.parse(birthDate);
sans vous soucier de DateTimeParseException parce que cette exception étend RuntimeException. Dès lors, on peut l'utiliser dans une expression lambda sans sourciller :
    persons.stream()
        .map(p -> LocalDate.parse(p.birthDate))
        .collect(toList());

Oui mais voilà, si à la place de parse() vous utilisez une bonne vieille méthode d'une autre classe qui lance une exception que vous être forcé à traiter, vous perdez énormément en lisibilité :
    persons.stream()
        .map(p -> {
            try {
                return p.getWhatever();
            } catch (WhateverException e) {
                Thrower.doThrow(e);
            }
        })
        .collect(toList());

Fort heureusement, la classe Thrower contient deux méthodes statiques qui peuvent être importées globalement permettant d'exécuter une méthode dont la signature contient des exceptions :
    persons.stream()
        .map(p -> (Whatever) safeCall(p::getWhatever))
        .collect(toList());

Le code source de la classe Thrower


Connaître la classe appelante d'une méthode



Il arrive qu'on veuille savoir quelle est la classe appelante d'une méthode. Peu importe que ce soit une bonne idée ou non, ça arrive, c'est tout.

Vous trouverez quelques utilisations dans les recettes suivantes ; cela permet par exemple de contraindre l'utilisation d'une méthode dans un contexte bien précis. Voici le code :
public class MyClass {

    public static void doSomething() {
        Class<?> callerClass = CF.getCallerClass();
        // do something with the caller
    }

    // allow to find the caller class
    private static class ClassFinder extends SecurityManager  {
        @SuppressWarnings({"unchecked", "rawtypes"})
        Class<?> getCallerClass() {
            Class[] classes = getClassContext();
            // call stack :
            // class org.example.MyClass$ClassFinder,
            // class org.example.MyClass,
            // class org.example.CallerClass,
            // ...]
            return classes[2];
        };
    }
    private static final ClassFinder CF = new ClassFinder();

}

Attention, si l'appel à getCallerClass() est relogé, il peut être nécessaire d'utiliser un autre indice dans la pile d'appel.


Renommer un Enum



Ce truc est particulièrement utile si on veut créer un Enum dont les valeurs utilisent des caractères interdits dans les noms Java. Pour contourner cela, nous utiliserons la méthode replace().

Prenons par exemple la spécification XPath ; celle-ci décrit 13 axes dont le nom contient un "-", ce qui est interdit pour un nom Java. A la place, on va écrire les valeurs avec un "_", mais le remplacer par le bon caractère dans le constructeur :
    // [6] AxisName   ::=   'ancestor' | 'ancestor-or-self' | 'attribute'
    //                    | 'child' | 'descendant' | 'descendant-or-self'
    //                    | 'following' | 'following-sibling' | 'namespace'
    //                    | 'parent' | 'preceding' | 'preceding-sibling' | 'self'
    public enum Axis {
            ancestor, ancestor_or_self, attribute,
            child, descendant, descendant_or_self,
            following, following_sibling, namespace,
            parent, preceding, preceding_sibling, self;
        Axis() {
            // replace the "_" in the name by a "-"
            replace(this, s -> s.replace('_', '-'));
        }
    }

Dans votre code vous pouvez référencer l'une des valeurs telle qu'elle est écrite dans l'enum, par exemple Axis.ancestor_or_self, et vous pouvez aussi obtenir la valeur avec son nom d'origine, par exemple :
    String axisName = "ancestor-or-self";
    Axis axis = Axis.valueOf(axisName);

Autre exemple avec un constructeur paramétré auquel on passe la valeur souhaitée :
    // MULTIPLICATIVE ::= '*' | '/'
    enum Multiplicative {
        MULT("*"), DIV("/");
        Multiplicative(String str) {
            replace(this, s -> str);
        }
    }

Et voici l'implémentation de la méthode replace() :
    public static void replace(Object enumValue, UnaryOperator<String> transformer) {
        // using enum class finder, see recipe #4
        Class<? extends Enum<?>> enumClass = ECF.getEnumClass();
        try {
            Field fieldName = enumClass.getSuperclass().getDeclaredField("name");
            AccessController.doPrivileged((PrivilegedAction<Void>) (() -> {
                fieldName.setAccessible(true);
                return null;
            }));
            String value = (String) fieldName.get(enumValue);
            value = transformer.apply(value);
            fieldName.set(enumValue, value);
            AccessController.doPrivileged((PrivilegedAction<Void>) (() -> {
                fieldName.setAccessible(false);
                return null;
            }));
        } catch (Exception e) {
            Thrower.doThrow(e);
        }
    }


Réordonner un Enum



J'ai même eu besoin de réordonner un enum :
public static enum Algorithm {

    SHA_256( 5 ),
    SHA_512( 6 );

    // ordinal used only in the MCF formatting
    Algorithm(int ordinal) {
        // replace the "_" in the name by a "-"
        replace(Algorithm.class, this, s -> s.replace('_', '-'));
        // replace the ordinal value
        reorder(this, o -> ordinal);
    }

}

L'implémentation de la méthode reorder() est semblable :
    public static void reorder(Object enumValue, UnaryOperator<Integer> transformer) {
        // using enum class finder, see recipe #4
        Class<? extends Enum<?>> enumClass = ECF.getEnumClass();
        try {
            Field fieldName = enumClass.getSuperclass().getDeclaredField("ordinal");
            AccessController.doPrivileged((PrivilegedAction<Void>) (() -> {
                fieldName.setAccessible(true);
                return null;
            }));
            int value = (int) fieldName.get(enumValue);
            value = transformer.apply(value);
            fieldName.set(enumValue, value);
            AccessController.doPrivileged((PrivilegedAction<Void>) (() -> {
                fieldName.setAccessible(false);
                return null;
            }));
        } catch (Exception e) {
            Thrower.doThrow(e);
        }
    }


Etendre un Enum



Comme dit en introduction, c'est une mauvaise idée d'étendre un Enum, et Java ne le permet pas : en effet, une valeur typée avec l'enum parent pourrait se voir affubler une instance de l'enum fils alors qu'elle n'est pas attendue puisque l'enum doit définir un ensemble connu de valeurs.

Si cela avait été permis, il aurait fallu revoir la notion d'extension dans ce cas particulier : étendre un enum signifierait non pas l'étendre comme une classe, mais utiliser ses valeurs dans un type séparé et non interchangeable avec le type d'origine. Et là ça marcherait.

Et bien, il se trouve que parfois, on a justement besoin d'étendre un enum à partir des valeurs d'un autre enum, sachant que l'extension dont il est question consiste simplement à "copier les valeurs dans un type séparé".

Tout d'abord, voyons les cas d'usage :
public enum A {
    b, c, d;
}
public enum Z {
    x, b, y;
    static {
        extend(A.class); // expect Z : { b, c, d, x, y }
    }
}

Pour info, la méthode extend() utilise l'astuce permettant de connaître la classe appelante, de sorte à pouvoir lier les 2 enums. Outre le fait qu'elle évite de passer 2 paramètres (l'enum de base et l'enum cible), elle impose une règle d'écriture forte : l'extension doit être faite à un seul endroit, dans l'enum cible.

Je ne vais pas mettre ici tout le code de la méthode extend(), parce qu'il est plutôt long, mais il doit permettre également d'étendre des valeurs qui implémentent une interface. Dans ce cas, ou si le comportement de la valeur de l'enum de base doit être repris, il faut que l'enum cible puisse être construit à partir de ces valeurs. Et si une valeur existe dans l'enum de base, il est possible d'altérer son comportement dans l'enum cible en la redéfinissant.

Dans un exemple concret cela donne ceci :
public enum Weekday {
    MON, TUE, WED, THU, FRI;
}

public enum WeekendDay {
    SAT, SUN;
}

public enum DayOfWeek {
    MON; // enum expect at least one value

    private boolean isWeekendDay = false;

    public boolean isWeekendDay() {
        return this.isWeekendDay;
    }

    private DayOfWeek() { }

    private DayOfWeek(WeekendDay weekendDay) {
        this.isWeekendDay = true;
    }

    static {
       extend(Weekday.class);
       extend(WeekendDay.class);
    }
    // expect MON, TUE, WED, THU, FRI, SAT, SUN
}

Côté implémentation, le problème est qu'il est impossible de construire une nouvelle valeur dans un enum. Le constructeur est protégé comme une citadelle et il nous faut recourir aux classes du paquet sun.reflect qui peuvent faire ce que l'on veut. Malheureusement, cela nous contraint à une forte dépendance avec ces classes internes, aussi un utilitaire intermédiaire est utilisé afin d'être substitué par une autre implémentation si nécessaire. Notez cependant que nombre d'outils Java utilisent ces classes internes avec moins de scrupules que moi...

Et voici le code source de la classe EnumUtil qui contient nos 3 utilitaires.

Notez qu'il y un inconvénient à cette pratique, c'est de ne pas pouvoir utiliser votre enum dans un switch case. Pour ma part, et parce que c'est une bonne pratique, je préfère directement solliciter les méthodes attachées aux valeurs de mes enums, ce qui fait que je n'ai que très rarement besoin de switch case.

Cela étant, je pense qu'il serait plus efficace d'utiliser un générateur de code.

Obtenir dynamiquement une instance d'une interface



On peut objecter que cela est inutile car il suffit d'écrire ceci :
SomeInterface instance = new SomeInterface() {
    // but you must have all the methods implemented here
};

Mais comme le dit si bien le commentaire :
  • vous devez fournir une implémentation de toutes les méthodes
  • d'autre part vous devez connaître à l'avance ladite interface
Comment obtenir une telle instance à partir d'une instance de Class qui s'avèrerait une interface ou une classe abstraite ?

Il s'agit clairement d'une problématique de génération de code. Pour ma part, j'ai créé un petit utilitaire qui permet d'obtenir un générateur (à la compilation) de générateur (à l'exécution) de bytecode d'une classe qui implémente ladite interface, ou qui étend une classe abstraite :
    @ByteCodeSpec
    private static ByteCodeFactory BYTECODE_FACTORY = ByteCodeFactory
        .getInstance("ml.alternet.util.ByteCodeFactory$"); // exist after code generation
    SomeInterface instance = BYTECODE_FACTORY.newInstance(SomeInterface.class);
ou
    SomeInterface instance = BYTECODE_FACTORY.getInstance(SomeInterface.class);

Evidemment, cela ne peut avoir un intérêt, j'imagine, que si SomeInterface.class est une variable.
Evidemment, aucune méthode de l'interface n'est implémentée, il est donc inutile de tenter de les utiliser.

On peut se demander à quoi ça sert ? Pour vous, je ne sais pas, mais en ce qui me concerne j'ai déjà utilisé cet outil. J'en parlerai dans un prochain billet.

Si le coeur vous en dit, je vous livre le code source :

Retourner un objet adapté au type du champ qui le recevra



Facile, on passe un objet Class parmi les paramètres de la méthode à appeler et le tour est joué. C'est aussi efficace que fastidieux. Parfois, le contexte d'appel nous donne suffisamment d'information pour pouvoir se passer de ce paramètre encombrant.

Je vais illustrer par un exemple ce que j'ai fait ; l'exemple est de permettre à l'utilisateur d'écrire une grammaire -Grammar- le plus simplement possible. J'ai trouvé qu'écrire des règles -Rule- et des valeurs terminales -Token- dans une interface était très commode ; notamment, on n'a pas besoin d'écrire pour chaque champ public static final ce qui rend la grammaire plus lisible. Vous voyez bien aussi que toutes les recettes précédentes ont également été utilisées... Bref, in fine, l'utilisateur doit obtenir une instance de sa grammaire afin de pouvoir parser du texte. J'ai fait les choix suivants :
  • j'ai conçu une méthode $() qui me donne l'instance de ce dont on a besoin ; j'ai écrit récemment un billet qui explique les vertus (controversées) du nommage pratiqué : quand on dispose d'une interface, générique, et qu'on veut fournir une implémentation par défaut, il est commode d'utiliser le nom de l'interface et de le postfixer par un '$', car les classes anonymes sont ainsi nommées en Java ; par mimétisme, la méthode $() signifie "donne moi une instance de cette classe"
  • comme dit précédemment, je ne veux pas passer de paramètre de classe à cette méthode, car je souhaite faciliter la vie de mes utilisateurs, qui n'ont pas à demander 2 fois ce qu'ils veulent (je veux une instance pour ce champ qui est de ce type, tiens, donne moi une instance je te passe le type : ça fait deux fois)
  • dans les grammaires, il y a parfois des règles qui appellent d'autres règles qui ne sont pas encore définies, ou des règles qui font référence à elles-mêmes ; dans un champ Java, on ne peut pas, j'ai introduit la notion de règle proxy, qui permet de déclarer la règle, puis plus tard de la définir. Pour la déclarer, j'utilise la méthode $()
Quand un utilisateur définit sa grammaire, il peut alors écrire ceci :
    Rule    myRule = $(); // retourne un objet de type Rule
    Grammar $      = $(); // retourne un objet de type Grammar

Cela permet à l'utilisateur de demander avec la plus grande simplicité une instance du type spécifié dans le champ qui le recevra. Comme la grammaire ne peux être instanciée qu'une fois tous les champs initialisés, elle est obligatoirement positionnée en dernière position. Je ne vais pas montrer comment est implémenté cette méthode parce qu'il y a pas mal de détails compliqués à régler, mais dans le principe il suffit d'obtenir la classe appelante (voir la recette #4), puis d'examiner si les règles sont toutes présentes (en partant par la fin pour échouer au plus tôt le cas échéant) : si c'est le cas, il suffit de générer une classe de la grammaire et d'en fournir le singleton (voir la recette #8), sinon on crée une règle proxy. Remarquez que bien que la signature de la méthode soit public static <T> T $() il n'y a pas moyen d'utiliser le type T générique car il sera effacé à l'exécution.

Tout cela, juste pour éviter d'écrire ceci :
    Rule    myRule = $(Rule.class);
    Grammar $      = $(Grammar.class);
Non, vraiment, je soigne mes utilisateurs :)

Protéger les mots de passe dans un serveur Web



Je ne peux pas vous laisser dire que vos serveurs Web sont bien sécurisés. Il y a de nombreuses traces laissées partout, tout le temps, durablement.

Chacun le sait, stocker un mot de passe dans une String est une très mauvaise pratique, comme dit dans ce billet passé : les String sont immutables, et une fois qu'on n'en veut plus, on ne peut pas en réinitialiser les caractères ; avant qu'ils ne soient collectés par le ramasse-miette, vos mots de passe résident dans la mémoire jusqu'à ce que la zone mémoire soit réallouée pour un autre usage ; il faut donc utiliser des char[] et prendre soin de bien tout nettoyer après utilisation.

L'objectif de la classe Password est de s'assurer que ces caractères ne soient pas exposés en clair trop longtemps, et surtout qu'une fois libérés le nettoyage attendu est bel et bien réalisé. Le risque en terme de sécurité est minime, car il faudrait pouvoir accéder à la mémoire pour voler un mot de passe.

Risque minime ? Pas tant que ça : les alertes récentes mentionnées dans la presse permettant d'accéder à un dump de la mémoire et de voler des données sensibles rendent l'usage de cette classe Password très pertinente. Attention, la classe Password n'est pas magique, elle se contente d'obfusquer les mots de passe qui séjournent en mémoire en les cryptant ; bien sûr il reste toujours possible de trouver la clé en mémoire, de trouver le mot de passe crypté, de trouver l'algorithmme à utiliser pour retrouver le mot de passe, mais c'est immensément plus difficile !!! Par ailleurs, lorsque le mot de passe est créé, puis lorsqu'il est utilisé, il faut bien qu'il existe en clair, il y aura toujours une petite fenêtre ou il apparaît tel quel ; le rôle de la classe Password est donc de réduire significativement la surface d'attaque, mais pas de l'annihiler.

Oui mais voilà, dans un serveur Web, les mots de passe sont traités avec des String : rien n'y fait, que vos utilisateurs s'authentifient par formulaire ou HTTP, les mots de passe séjournent non seulement en String, mais aussi également sous d'autres formes, et, circonstance aggravante, pendant une durée longue, vraiment très longue : de l'ordre de la seconde, ce qui à l'échelle de la vitesse du CPU est interminable. Voyons en détail ce qui se passe quand on soulève le capot :
  1. un flot d'octets vient d'arriver dans un buffer : il correspond à une requête HTTP d'authentification, ou à un formulaire d'enregistrement de compte ; bref, il contient un mot de passe encodé en Base64 pour le premier cas ; donc en clair dans les 2 cas.
  2. les octets sont convertis en caractères : une fois convertis, les octets d'origine contenant le mot de passe restent tels quels avec le mot de passe en clair. Pire : si les buffers de caractères alloués sont trop petits, de nouveaux buffers plus grands sont créés et reçoivent les données jusque là écrites ; mais les buffers précédents restent avec leurs données
  3. le serveur de servlet traite les authentifications avec des String, et les données de formulaire avec des String : le mal est fait
  4. pour une création de compte, le mot de passe est haché avec PBKDF2 ou BCrypt, ce qui prend pas loin d'une seconde, ensuite il est stocké en base de données ; pour une authentification, il peut également être haché pour comparaison, ou transmis tel quel au service d'authentification, par exemple un serveur LDAP. Avant d'être hachés, les caractères sont encodés par des encodeurs qui ne nettoient pas les données intermédiaires, notamment si la zone allouée est trop petite.
  5. on libère le String qui a servi
On voit bien que les mots de passe résident sous plusieurs formes (buffers d'octets, buffers de caractères, String) mais peuvent également être amenés à être dupliqués.

Pour corriger tout ça, il faut agir dès le début, et s'assurer que toutes les étapes ne causent pas une fuite de nos données sensibles de bout en bout. Je ne vais pas montrer de code car la correction qui prend en charge le problème est assez lourde, mais voici ce qui a été fait :
  1. le buffer d'octets en entrée est intercepté et analysé sans transformation en caractères afin d'isoler les zones susceptibles de contenir des mots de passe (en-têtes HTTP et champs de formulaire ciblés par un paramétrage approprié)
  2. ces zones sont converties en Password et les octets effacés : ceux-ci deviennent inutilisables par la suite, et peu importe qu'ils soient maltraités comme auparavant
  3. on injecte dans le serveur de servlet une mécanique permettant d'utiliser les instances de Password en lieu et place où les données doivent être traitées ; par exemple pour récupérer les données d'un formulaire d'inscription, on écrira simplement dans une application REST avec JAX-RS :
    @POST
    public String doRegister(@FormParam("username") String userName,
                             @FormParam("pwdField") Password pwd) {
        // ...
    }
  4. s'il faut hacher les mots de passe, on utilise un petit utilitaire SafeBuffer qui nettoie toutes les données intermédiaires lors de la conversion des caractères en octets
Une fois libérées, toutes les zones mémoires qui ont vu passer un mot de passe sous quelque forme que ce soit ont été nettoyées au fur et à mesure.

Le hack utilisé ici s'applique à des serveurs tels que Tomcat et Jetty. Vous trouverez tous les détails techniques dans les pages du projet Alternet Security et également des démos avec authentification LDAP qui peuvent être testées.

Vous y trouverez également un framework d'authentification basé sur ces fameux Passwords.

Concernant le hachage des mots de passe, la littérature recommande d'utiliser des algorithmes forts tels que BCrypt ou PBKDF2 SHA256 / SHA512. Quand on regarde à l'intérieur, on constate malheureusement que lors du processus itératif (celui qui fait que ça dure longtemps), le mot de passe est utilisé, donc maintenu en mémoire pendant toute cette durée. Je recommande d'utiliser à la place Argon2, car le mot de passe n'est utilisé qu'à l'initialisation du hash, et peut donc être effacé avant le lancement des itérations ; de fait, la surface d'attaque (durée d'exposition en clair du mot de passe) est considérablement réduite.

Argon2 est également disponible dans le framework d'authentification d'Alternet Security. Notez que les seules modifications appliquées portent sur l'utilisation de la classe Password pour récupérer les octets. Le fonctionnement intime des divers algorithmes n'est pas modifié.






Semantic Mismatch
ERR-19 : IllegalArgumentException
Unable to set NAGER between DEUX EAUX
-- In "Nager entre deux eaux"
-- See log for details



Aucun commentaire:

Enregistrer un commentaire