confusable-homoglyphs : une bibliothèque pour gérer les caractères qui se ressemblent

Posté par (page perso) . Édité par Xavier Teyssier, Davy Defaud et Benoît Sibaud. Modéré par Pierre Jarillon.
39
14
mar.
2019
Java

Bonjour tout le monde,

J’ai l’honneur de vous présenter une nouvelle bibliothèque Java (en fait compatible avec n’importe quel langage qui utilise la machine virtuelle Java, normalement) : confusable-homoglyphs. « Nouvelle » dans sa version Java, car c’est en réalité un portage d’une bibliothèque Python. Notez qu’il en existe aussi une version PHP.

C’est un outil sous licence Apache 2.0 qui permet de gérer directement au sein d’une application les caractères Unicode qui se ressemblent, et donc qui sont source de confusion.

Quel est donc le but de cette bibliothèque ?

Les humains utilisent divers systèmes d’écriture, et ce qui devait arriver, arriva : il y a des caractères qui se ressemblent (voire qui sont graphiquement identiques, selon la police d’écriture), mais qui sont différents. Or, de telles confusions peuvent avoir des conséquences fâcheuses, par exemple :

  • l’utilisateur « ΑlaskaJazz » (le premier Α est un alpha majuscule grec) peut se faire passer pour l’utilisateur « AlaskaJazz » (uniquement des lettres latines) ;
  • on peut vous inciter à vous connecter sur www.microsоft.com ou www.faϲebook.com au lieu de www.microsoft.com ou www.facebook.com ;
  • etc., on pourrait multiplier les exemples désagréables à l’infini.

Si Unicode fournit des listes des caractères qui peuvent prêter à confusion, elles sont difficiles à utiliser en l’état : ce sont des fichiers textes avec un formatage particulier, qui plus est hébergés sur des serveurs instables et fréquemment hors ligne.

confusable-homoglyphs permet de gérer ces confusions en détectant et donnant des informations sur les caractères potentiellement problématiques.

D’accord, mais comment ça s’utilise ?

La bibliothèque est disponible sur Maven Central et donc s’importe comme n’importe quelle autre bibliothèque Java, ici avec Maven ou Gradle :

<dependency>
    <groupId>fr.spacefox</groupId>
    <artifactId>confusable-homoglyphs</artifactId>
    <version>1.0.1</version>
</dependency>
compile group: 'fr.spacefox', name: 'confusable-homoglyphs', version: '1.0.1'

Puis on initialise l’une des deux classes utilitaires, selon ce qu’on veut faire :

Categories categories = Categories.fromInternal();
Confusables confusables = Confusables.fromInternal();
// ou si on préfère fournir un jeu de données spécifique :
Categories categories = Categories.fromJson("/full/path/to/categories.json");
Confusables confusables = fromJsons("/full/path/to/categories.json", "/full/path/to/confusables.json");
// (Confusables utilise Categories en interne).

Et on utilise les objets créés. Il y a un résumé de l’API publique ici. La création est relativement longue par rapport à l’utilisation, il vaut mieux garder ces objets vivants entre deux appels.

Les intentions et contraintes du projet

En réalité, je n’ai pas besoin de cette bibliothèque, au‐delà du fait que je trouve qu’elle manque à Java. Je voulais aussi et surtout :

  • connaître la difficulté à porter une bibliothèque de Python à Java ;
  • voir ce que c’est de créer un projet, seul, de A à Z et complet (avec documentation et tout) ;
  • essayer de déployer quelque chose sur Maven (en tant que fournisseur).

Les contraintes que je me suis choisies pour ce projet étaient donc :

  • licence libre (Apache 2.0) ;
  • Java 8, pour garder une bonne compatibilité avec les projets actuels et les différents langages qui utilisent la machine virtuelle Java ;
  • une API aussi proche de possible que l’originale (il y aura une version 2 avec des modifications dans cette API, un jour) ;
  • disponible sur Maven Central, pour être utilisable comme n’importe quelle bibliothèque Java ;
  • avec une doc complète ;
  • avec une couverture de tests correcte ;
  • avec de l’intégration continue via GitHub (j’ai toujours utilisé GitLab et GitLab CI pour mes autres projets Java).

Et comme j’aime bien partager mon expérience, j’ai écrit un billet sur ce que j’ai tiré de celle‐ci.

Je n’ai jamais eu l’occasion de vérifier que tout fonctionne bien sous Android. Si quelqu’un s’en sert dans cet environnement, que cette personne n’hésite pas à me faire signe. J’espère que ce petit utilitaire – ou les versions Python ou PHP – pourront vous servir un jour ! N’hésitez pas à créer tickets et demandes d’intégration (pull requests) si le cœur vous en dit !


Cette dépêche est publiée sous licence CC BY 4.0.

Aller plus loin

  • # Microsоft.com, Faϲebook.com

    Posté par . Évalué à 8. Dernière modification le 14/03/19 à 14:14.

    La confusion tient beaucoup de la police de caractère :

    Champ de recherche de DuckDuckGo

    Dans l'article de la dépêche il n'était pas possible de voir la différence (Firefox/W7).

    • [^] # Re: Microsоft.com, Faϲebook.com

      Posté par (page perso) . Évalué à 10.

      Certes, mais beaucoup de gens n'y font pas attention ! Voire, n'ont aucune idée de ce qui est possible de faire.

      Le nombre de gens pourtant n'ayant jamais connu de machine à écrire qui écrivent des O majuscule à la place des zéros est assez consternant. Et ces personnes ne voient pas les différences qui, pourtant, sautent aux yeux (aux miens, en tout cas), alors pour des caractères « exotiques ».

      Et, effectivement, dans le texte de la dépêche, les caractères sont visuellement identiques.

      OS préféré Mageia 6 et Mageia 7, CMS préféré SPIP, suite bureautique préférée LibreOffice, logiciel de dessin préféré Inkscape.

      • [^] # Re: Microsоft.com, Faϲebook.com

        Posté par . Évalué à 5.

        Le nombre de gens pourtant n'ayant jamais connu de machine à écrire qui écrivent des O majuscule à la place des zéros est assez consternant.

        C'est vrai qu'on voit ça souvent. A leur décharge, ces 2 caractères sont contigus sur le layout Azerty et s'obtiennent en pressant SHIFT. Un petit dérapage et c'est l'erreur. Je crois même que ça m'est déjà arrivé (bon j'ai corrigé parce que j'y suis sensible mais la plupart des gens n'y attache pas grande importance).

    • [^] # Re: Microsоft.com, Faϲebook.com

      Posté par (page perso) . Évalué à 4.

      Dans l'article de la dépêche il n'était pas possible de voir la différence (Firefox/W7)

      C'est aussi vrai avec Firefox avec Mageia. C'est justement l'objet de cet article.
      La confusion entre deux glyphes de même apparence est utilisée par des tas d'escrocs !

      Ce serait bien que l'affichage des URL contenant des caractères non ascii soit systématiquement contrôlé et que les caractères hors ascii soient colorés d'une façon très perceptible.

      • [^] # Re: Microsоft.com, Faϲebook.com

        Posté par (page perso) . Évalué à 10.

        Ce serait bien que l'affichage des URL contenant des caractères non ascii soit systématiquement contrôlé et que les caractères hors ascii soient colorés d'une façon très perceptible.

        Alors ça sûrement pas. C'est la pire des réponses au problème qui pourrait être faite. Et malheureusement c'est le genre de réponses qui est souvent faite par certains développeurs des pays occidentaux et ça explique probablement en partie le peu d'intérêt des logiciels libres dans pas mal de pays (en tous cas, dans les pays extrême-orientaux, je peux attester que le libre est très anecdotique par exemple). De nos jours, pas mal de trucs se sont vachement améliorés heureusement, mais par exemple pendant longtemps, les difficultés pour installer les méthodes d'entrée pour langue non européennes rendaient évident que des barrières étaient créées dès l'installation d'un OS libre.

        Donc pour bien revenir au sujet, non pas tout le monde n'utilise les caractères latin (encore moins le sous-ensemble ASCII) et il n'y a aucune raison pour l'imposer au monde entier.

        Ainsi si on prend les exemples: Αlaska est problématique, oui, mais parce que le reste des lettres est dans l'alphabet latin et surtout "Alaska" est un nom propre en anglais.
        Mais si maintenant le mot était Αλάσκα (qui apparemment veut dire Alaska en grec, cf. Wikipédia) alors le Α (alpha majuscule grec) est attendu et ce serait le A ASCII qui serait problématique, et possiblement utilisé pour tromper autrui (pas forcément, notez bien, mais "possiblement").

        Ensuite on pourrait imaginer des cas où mélanger les alphabets/syllabaires peut être acceptables, que ce soit pour des jeux de mots ou du character art (beaucoup utilisent des scripts non locaux pour leur forme, j'en ai repéré sur linuxfr même, au fil des ans). Ou simplement on pourrait imaginer un franco-grec avec un prénom composé (un nom français, un grec, ça ne me paraît pas improbable), etc. Donc ce n'est pas forcément un problème simple à résoudre. Mais si on veut gérer ces problèmes, c'est par ce type de réflexion et pas en disant "tout en ASCII". On est dans un monde Unicode, et c'est une avancée, pas de retour en arrière SVP! :P

        De mémoire, il me semble qu'il existe même plusieurs RFCs qui évoquent ce type de problèmes (notamment pour la problématique des noms de domaine acceptables), et ce sur de nombreuses pages (je peux assurer qu'aucune de ces RFC ne dira juste "utilisez tous ASCII", surtout en s'adressant à des pays dont la langue n'utilise pas les caractères latins!).

        Sinon, et pour parler un peu de la librairie de l'article, en jetant un œil au README du dépôt, il semblerait que cette bibliothèque gère certains trucs intéressants. Déjà je vois les notions de mixed script, etc. J'ai pas cherché beaucoup plus loin, mais ça a l'air correct d'un rapide coup d'œil. :-)

        Film d'animation libre en CC by-sa/Art Libre, fait avec GIMP et autre logiciels libres: ZeMarmot [ http://film.zemarmot.net ]

        • [^] # Re: Microsоft.com, Faϲebook.com

          Posté par . Évalué à 1.

          Je sais pas, sans vouloir être pour la domination d'une langue en particulier, lire du code dans un alphabet utf-8, peut s'avérer ardu, surtout si le jeu de caractère n'est pas présent/dispo sur la machine.

          L'alphabet latin est connu d'à peu près tout le monde (les japonais l’apprennent, même s'il n'est pas le leur), il à l'intérêt de ne pas avoir de collision ou peu (i, 1 I l |, qui sont facilement visible avec les polices de codage)

          Pour les url, je serai pour avoir un indicateur visuel dès que des caractères sortent du groupement majoritaire, et un indicateur différent selon l'origine du caractère, on a je ne sais combien de a en utf-8, mais cela ne s'arrête pas la, on peut jouer avec les . les / et autre éléments constitutifs des url.

          D'un point de vue purement codage, sémantique et autre, se limiter a 255 caractère simplifie beaucoup l'écriture des lexeurs et on à moins de cas particulier à gérer (et encore, si on se limite à l'ascii commun (sans accent)), on simplifie encore plus, un nom de fonction/variable c'est [a-zA-Z0-9_]+; si on se met à gérer tous les alphabet existant, on doit gérer les espaces (quadratin, insécable, autre), les barres obliques, séparateurs, mais bon c'est mon coté flémouille qui s'y colle…

          Le soucis c'est qu'en jouant assez bien on doit très facilement pouvoir ajouter des backdoor rien, qu'en trichant sur les héritages ou affectation de variable à la graphie similaire.

          Ensuite on pourrait limiter les fichiers de code à un ensemble de caractère correspondant à une langue; pour les url colorier les caractères des url en fonction des risque des confusion (latin, hiragana, katakana => bleu; arabe, grec => vert…)

          On est dans un monde Unicode

          nan utf-8 et c'est la chianli, impossible de savoir facilement combien d'octet fait ta chaine de 15 caractères, la différence de perf entre un LANG=C grep et grep tout court est assez impressionnante; on devrait utiliser du utf-16 ou utf-32; où les caractères sont de taille fixe.

          Il ne faut pas décorner les boeufs avant d'avoir semé le vent

          • [^] # Re: Microsоft.com, Faϲebook.com

            Posté par (page perso) . Évalué à 2.

            nan utf-8 et c'est la chianli

            UTF8 c'est un format de stockage relativement optimal mais qui complique les traitements. Rien ne t'empêche de passer vers un format plus pratique lorsque tu manipules des chaînes dans ton programme, certains langages le font.

            Python 3 - Apprendre à programmer en Python avec PyZo et Jupyter Notebook → https://www.dunod.com/sciences-techniques/python-3

          • [^] # Re: Microsоft.com, Faϲebook.com

            Posté par (page perso) . Évalué à 2. Dernière modification le 19/03/19 à 21:56.

            J'ai déjà répondu au gros du message plus bas, avant que tu ne le postes. Je trouves ça triste de vouloir restreindre l'incroyable richesse des langues mondiales, et de vouloir imposer un système d'écriture qui n'est pas celui de l'incroyable majorité de la population mondiale (pas même du français, à cause des diacritiques).

            Cela dit :

            on devrait utiliser du utf-16 ou utf-32; où les caractères sont de taille fixe.

            Les caractères UTF-16 n'ont pas une taille fixe (même si ce n'est pas la même gestion qu'avec UTF-8).

            PS : tu mélanges un peu tout. Unicode a prévu des classes de caractères. Pour la programmation, il suffit de n'accepter comme identifiants que ce qui est un caractère réel, pas un symbole ou une espace etc.

            La connaissance libre : https://zestedesavoir.com

            • [^] # Re: Microsоft.com, Faϲebook.com

              Posté par . Évalué à 2.

              Je trouves ça triste de vouloir restreindre l'incroyable richesse des langues mondiales, et de vouloir imposer un système d'écriture qui n'est pas celui de l'incroyable majorité de la population mondiale (pas même du français, à cause des diacritiques).

              Yep, c'est triste d'avoir un langage qui impose des mots clés ascii, et anglais qui plus est, mais y a tellement de moyen de tricher avec l'utf-8…

              Pour la programmation, il suffit de n'accepter comme identifiants que ce qui est un caractère réel,

              yep, puisque tu parles de python

              #/usr/bin/python
              import getopt
              import sys
              
              output_filename = 'default.out'
              
              #print 'ARGV      :', sys.argv[1:]
              
              options, remainder = getopt.getopt(sys.argv[1:], 'o:v', ['output=', 
                                                                       'verbose',
                                                                       'version=',
                                                                       ])
              print( 'OPTIONS   :', options)
              
              for opt, arg in options:
                  if opt in ('-o', '--output'):
                      оutput_filename = arg
              
              print( "et l'heureux gagnant est "+ output_filename)
              print( "et l'heureux gagnant est "+ оutput_filename)

              facile à tester et c'est marrant on a pas 2 fois la même ligne! Ça l'est moins si c'est un uid, ou un élément critique du système

              Note bien j'ai proposé une solution simple : n'accepter qu'un jeu restreint de caractères par fichier, afin d'éviter ce genre de blague, mais ça devient galère lorsque les mots clés du langage sont dans un alphabet proche.

              Il ne faut pas décorner les boeufs avant d'avoir semé le vent

          • [^] # Re: Microsоft.com, Faϲebook.com

            Posté par (page perso) . Évalué à 3.

            L'alphabet latin est connu d'à peu près tout le monde (les japonais l’apprennent, même s'il n'est pas le leur), il à l'intérêt de ne pas avoir de collision ou peu (i, 1 I l |, qui sont facilement visible avec les polices de codage)

            Le Japon est un mauvais example. Ils sont très attirés par l'anglais (même s'ils ne le parlent pas bien), au point d'avoir de l'anglais dans un nombre impressionant de shows/quiz à la télé, et que les écoles de langues pullullent. N'oublions pas que les USA se sont aussi imposés physiquement au Japon après la seconde guerre.

            Je serais plus interessé de qu'on parle de langues de pays ou l'anglais est moins bien vu et/ou moins présent.

            • [^] # Re: Microsоft.com, Faϲebook.com

              Posté par . Évalué à 2.

              Je serais plus intéressé de qu'on parle de langues de pays ou l'anglais est moins bien vu et/ou moins présent.

              Le soucis c'est que les langages de programmations sont intrinsèquement anglais (dans leur immense majorité) (class, interface, private, struct, if, for, while, overide, implements, int, float, long, Integer, import, include, iterator, map…) et nécessite donc des bases de l'alphabet latin; qu'un langage d'origine coréenne refuse les caractères latins comme identifiants des variables/fonctions ne me choquerait pas

              mixer 2 alphabets pour coder ça risque d'être marrant, surtout si les sens de lecture sont différents)

              y'a déjà eu ce genre de question posé, et il existe notamment un langage arabophone:

              https://www.developpez.com/actu/51298/Un-scientifique-sort-un-nouveau-langage-de-programmation-en-arabe-pour-demystifier-l-art-de-coder/

              A supposer qu'on puisse coder dans un autre alphabet, en gardant les mots clé latin, soit, mais cela n'a aucun sens de mixer 2 alphabets pour les identifiants, et devrait, à minima, donner un warning.

              Il ne faut pas décorner les boeufs avant d'avoir semé le vent

      • [^] # Re: Microsоft.com, Faϲebook.com

        Posté par (page perso) . Évalué à 1.

        La confusion entre deux glyphes de même apparence est utilisée par des tas d'escrocs !

        Il n’y a pas de lien évident entre l’hameçonnage et les IDN.

        • [^] # Re: Microsоft.com, Faϲebook.com

          Posté par . Évalué à -2.

          Il y a eu quelques cas sur Whatsapp par exemple.

          • [^] # Re: Microsоft.com, Faϲebook.com

            Posté par (page perso) . Évalué à 2.

            L’exemple donné est tellement évident que l’attaquant aurait pu mettre login.paypal.pas-du-tout-du-phishing.example, ça n’aurait rien changé.

            Et puis quelle différence entre ça et un mail en HTML avec un lien du style : [login.paypal.com](login.paypal.phishing.example) ?

            • [^] # Re: Microsоft.com, Faϲebook.com

              Posté par . Évalué à 1.

              Mon précédent message n'était en rien une prise de position. J'ai simplement déjà vu des tentatives (un autre exemple jouait sur sur une lettre semblable du l de lidl.com)

              En ce qui concerne la remarque suivante:

              Et puis quelle différence entre ça et un mail en HTML avec un lien du style : [login.paypal.com](login.paypal.phishing.example) ?

              Un lien de ce style est plus facilement détectable puisque l'url sur lequel on se connecte n'est pas un homographe (on s'en rend compte en passant la souris dessus, en regardant la barre d'adresse, etc.). C'est plus difficile avec Ιidl.com (la première lettre est un iota majuscule ; ça se voit mieux avec le formatage "code" : Ιidl.com, du moins sur la machine que j'utilise).

        • [^] # Re: Microsоft.com, Faϲebook.com

          Posté par . Évalué à 1.

          C'est un article qui date de 2009 : en 10 ans tout a eu le temps de tellement évoluer.

          BeOS le faisait il y a 15 ans !

      • [^] # Re: Microsоft.com, Faϲebook.com

        Posté par (page perso) . Évalué à 3.

        Sans parler d’escrocs, j’ai rencontré récemment un problème sur l’importation d’un simple fichier CSV suite à la confusion entre un × (multiplication) et un x (X minuscule).

  • # Quelques précisions…

    Posté par (page perso) . Évalué à 8. Dernière modification le 15/03/19 à 17:24.

    Hello,

    Déjà l'honnêteté intellectuelle m'oblige à préciser qu'en réalité,tant qu'on parle de la JVM ICU permet déjà ce genre de chose (en plus complet mais avec une API plus compliquée, et sans garantie quant à la gestion des catégories, cf ci-dessous).

    Et quelques précisions pour répondre aux commentaires ci-dessus.

    Je précise que l'outil fait de la détection multi-caractères : © et (c) vont être considérés comme pouvant être confondus (dans les deux sens).

    Bien évidemment, ce genre d'outil ne doit servir en aucun cas à forcer du simple ASCII. Unicode est un progrès gigantesque dans un monde où les outils de l'état massacrent encore les noms et prénoms de ses propres citoyens (ceux qui ont une diacritique dans leur nom ou prénom savent à quel point elle est souvent supprimée). Ne nous servons pas de ces possibilités pour régresser, merci.

    C'est pour ça qu'il y a des outils pour récupérer des alias et surtout des catégories sur les caractères ; et surtout que les outils de gestion des confusions permettent de gérer des catégories de caractères « de base ». Par exemple pour une application en Français, on voudrait d'autoriser / ne détecter les risques de confusion qu'au sein d'une classe de caractères. Par exemple, une application en français voudrait pouvoir éviter les chaines qui contiennent des caractères que l'on peut confondre avec des caractères latins mais qui n'en sont pas (les caractères latins sont considérés comme légitimes). Une application en russe voudrait elle que ce soit les caractères cyrilliques qui sont légitimes. Je vous renvoie à la doc pour plus de détails1

    Aujourd'hui ce qu'il manque à confusables-homoglyphs, c'est l'équivalent des skeleton de ICU, c'est-à-dire une fonctionnalité qui permet d'enregistrer une version « normalisée » d'une chaine de caractères. Cette version n'est pas utilisable à l'affichage, mais permet des comparaisons : deux chaines de même squelette peuvent être confondues. En enregistrant le squelette dans un champ ad hoc, on peut autoriser par exemple des pseudonymes avec n'importe quels caractères, tout en interdisant facilement tout autre pseudonyme homoglyphe.

    Enfin, tout l'intérêt des exemples était de montrer que dans certains cas, il n'y a absolument aucun moyen graphique de discerner deux homoglyphes. En réalité l'exemple des noms de domaines était repiqué de la doc d'origine, mais est assez mauvais parce que les navigateurs vont afficher le nom de domaine canonique, donc http://www.xn--faebook-6pf.com/ et http://www.xn--microsft-sbh.com/ – en plus du fait que les utilisateurs font rarement attention à ce qu'il y a dans la barre d'URL.


    1. Celle du projet Python d'origine, j'ai oublié de publier la doc Java… 

    La connaissance libre : https://zestedesavoir.com

  • # Commentaire supprimé

    Posté par . Évalué à -1. Dernière modification le 17/03/19 à 14:08.

    Ce commentaire a été supprimé par l'équipe de modération.

Suivre le flux des commentaires

Note : les commentaires appartiennent à ceux qui les ont postés. Nous n'en sommes pas responsables.