Espace de code sécurisé

Je me souviens avoir lu un article un jour, qui recensait les aberrations physiques dans Star Wars. On y expliquait notamment qu’il était impossible de déployer des vaisseaux aussi imposants hors d’une planète, ou encore, plus prosaïquement, que l’absence de matière dans l’espace rendait la propagation du bruit impossible. Les chasseurs impériaux n’auraient dès lors pas dû pousser des râlements de chat souffrant de pharyngite aiguë.

Pourtant, le succès de la saga n’aurait pas été aussi colossal sans ces nécessaires entorses à la réalité, partie intégrante de la magie de son univers et stimuli indéniables de notre imagination. Après tout, il s’agit d’une œuvre de l’esprit, et, comme chacun sait, la physique s’applique partout dans l’univers, si ce n’est dans un recoin très sombre où aucune règle n���est figée: notre pensée.

Il est facile d’imaginer que notre intelligence étant purement éthérée, elle ne se soumette aucunement aux lois qui touchent la matière telles que la physique les décrit en tant que science. Certaines coïncidences, qu’il est certes difficile de trouver fondées, ébranlent néanmoins cette frontière pourtant imperméable, évoquant des ressemblances étonnantes entre le comportement de la matière et le comportement humain.

Ainsi, par exemple, le principe d’inertie appliqué en mécanique et qui explique la résistance d’un corps face à une modification de son mouvement, rappelle étrangement la résistance au changement que nous connaissons tous en tant qu’individu. Inutile de parler de la gravité qui pousse deux corps à s’attirer réciproquement, ou encore de l’équation de Schrödinger qui seule peut expliquer l’attrait de mon chat pour les boîtes en carton.

Il en va de même de l’entropie sans laquelle on ne pourrait expliquer la vitesse à laquelle notre code devient un vrai foutoir. L’entropie est sans doute la force qui nous pousse à sombrer du côté obscur. C’est le Dark Vador des applications.

L’entropie dans Javascript

On le sait, le Javascript n’est pas un langage orienté objet dont le paradigme permet de regrouper le code en classe, à l’instar de Java, PHP, etc. Il s’agit d’un fonctionnement particulier, que certains aiment et d’autres moins, basé sur le prototypage.

Cette architecture émane directement de la volonté de ses concepteurs de créer un langage de script simple, facile et accessible à tous. Cette philosophie de base est des plus légitimes. Un des points forts du web demeure dans son accessibilité, bien adaptée à des sites modestes tels qu’ils composent une part importante du web.

Toutefois, force est de constater que notre code Javascript a tendance à se complexifier avec le temps, et à devenir très difficilement maintenable… Au final, il se transforme en une accumulation de variables et de fonctions, clairsemées entre plusieurs fichiers où s’entremêlent parfois des fonctionnalités différentes, et où les dépendances gangrènent toute possibilité d’évolution future.

  • Les fonctionnalités se mélangent.
  • Les portées de codes différents se chevauchent.
  • Il est difficile d’entrer dans ce code, et de connaître le contexte d’exécution.
  • Le refactoring du JS est compliqué.
  • Il est difficile de recenser le code mort.
  • Toute modification de HTML est compromise par la crainte d’un effet de bord.

 

Principes d’interaction faible

Face à cette tendance inévitable, seule une bonne organisation du code peut garantir souplesse et évolutivité.

Une des premières règles consiste à maximiser l’indépendance par rapport au HTML. Il existe une série de solutions, allant de simples best-practices à l’utilisation de frameworks MVC comme Backbone.js ou le prometteur Meteor.js. Nous n’allons toutefois pas aborder ce point qui dépasse le sujet de cet article.

L’autre bonne pratique, simple et purement liée au Javascript, consiste à sécuriser son espace de travail. Il s’agit de regrouper son code en fonctionnalités dont les interactions avec l’extérieur sont identifiées et contrôlées au mieux.

Les fonctions IIFE

Sans conteste, la première bonne pratique en la matière consiste à regrouper chaque fonctionnalité au sein d’une même fonction IIFE. Une fonction IIFE (Immediately-Invoked Function Expression) est une simple fonction, telles que nous les utilisons tous les jours, si ce n’est qu’elle est appelée automatiquement. L’ensemble du code d’une fonctionnalité peut y être transposé sans problème (fonctions, variables et objets), ce qui permet de scinder notre application en briques de base bien délimitées et recyclables.

Cette encapsulation du code empêche également les variables déclarées au sein de la fonction d’être visibles depuis l’extérieur. Si toutes les fonctionnalités d’une application sont traitées de la sorte, elles demeureront isolées et donc protégées les unes des autres. L’espace global n’est pas pollué par une multitude de variables qui seraient visibles à travers toutes les fonctionnalités, même lorsque celles-ci ne les utilisent pas.

En s’astreignant à toujours passer les variables externes au travers des arguments de la fonction, nous pouvons facilement identifier les dépendances du code envers l’extérieur. Nous contrôlons ainsi au mieux la portée des variables externes.

Bien sûr, l’inverse n’est malheureusement pas vrai. Les variables externes demeurent visibles à l’intérieur de la fonction. Aucun stratagème ne peut, à ma connaissance, totalement isoler une fonction Javascript de l’extérieur.

Le mode strict

Un peu plus haut, j’indiquais que les variables internes n’étaient pas accessibles depuis l’extérieur. Ceci n’est pas tout à fait exact. En effet, si, toujours à l’intérieur d’une fonction, je ne déclare pas ma variable à l’aide du mot-clé var, elle sera automatiquement assignée comme attribut de window. Elle pourra ainsi, bien naturellement, être appelée depuis l’extérieur de la fonction, comme tout autre attribut de window. C’est ce qu’on appelle, par abus de langage, une variable globale.

Il existe heureusement une façon d’obliger la déclaration de toute variable, en utilisant le mode strict (strict mode) d’ECMAScript 5. Ce mode, encore peu connu et donc trop peu usité, contraint Javascript à signaler une série d’erreurs potentiellement dangereuses et habituellement permises par le langage. Pour ce faire, il suffit de déclarer la directive (on parle parfois de pragma) « use strict » au début d’une fonction.

A noter qu’il est possible de déclarer le mode strict au début de l’espace global pour qu’il s’applique à l’ensemble du code. Cette méthode n’est toutefois pas conseillée car elle pourrait altérer le comportement des bibliothèques de code externes dont on ne maîtrise pas l’implémentation. jsHint et jsLint s’attendent d’ailleurs à trouver, par défaut, le mode strict activé localement au sein des fonctions, et signalent une erreur s’il est demandé au niveau global.

Enfin, nous ne devons pas nous inquiéter de la compatibilité ascendante du mode strict. Etant donné que cette directive est spécifiée à l’intérieur de simples ou de doubles guillemets, les moteurs Javascript antérieurs à ES5 et qui ne peuvent interpréter une telle commande, ne lanceront pas d’erreurs, croyant lire une simple String.

Les espaces de noms

Un problème peut alors se poser. Puisque je ne vois plus les variables depuis l’extérieur, comment utiliser ma fonctionnalité en dehors de son bloc de code?

Une procédure simple existe pour répondre à cette nécessité, qui permet de garder un code clair tout en rendant accessible une sorte d’API spécifique à la fonctionnalité. Elle vise à imiter les espaces de noms dédiés à un package, en l’émulant via le système traditionnel d’accès aux attributs d’un objet.

En Javascript, window est accessible partout et constitue par ailleurs -du moins si le mode strict n’est pas activé- le contexte de toute fonction qui n’est pas rattachée à un objet. Window est le point commun entre la fonction et l’espace global.

Dès lors, window peut accueillir des attributs spécifiques qu’on lui assigne depuis la fonction et qui vont constituer ainsi une liste de packages renfermant les fonctionnalités.

La gestion des erreurs

Il est très facile, surtout avec le mode strict activé, que des erreurs soient renvoyées par le code, auxquelles nous n’aurions pas pensé tout de suite. Or, une fois en production, le comportement de l’application côté client échappe souvent à tout contrôle du développeur. Par défaut, si une erreur se produit, seul l’utilisateur s’en aperçoit, de préférence en jurant sur un site si mal conçu…

En Javascript, une erreur est, en réalité, une exception qui étend la classe Error. Sur les différents types d’erreur, je ne peux que vous recommander de parcourir l’article de Colin Ihrig. Il est donc assez aisé d’intercepter l’erreur, si ce n’est même de la loguer.

Un projet intéressant, Glitch.js, vise à utiliser un wrapper qui fait à la fois office de fonction IIFE et de catcher d’erreur.

Conclusions

Un espace de code devient sécurisé lorsque l’on en connait l’étendue, les dépendances,  et les interférences. Seule une politique de régulation claire des entrées et sorties peut minimiser les effets de bords et garantir la maintenance des fonctionnalités.

Cette gestion du code peut se résumer en quatre bonnes pratiques:

  1. Les fonctionnalités sont regroupées en un même bloc de code, de préférence une fonction IIFE.
  2. Les variables externes nécessaires à la bonne exécution du code sont identifiées dans les paramètres de la fonction.
  3. Les variables ne sont accessibles depuis l’extérieur de la fonction que par l’intermédiaire de namespaces.
  4. Les erreurs de la fonction sont rattrapées avant de contaminer les autres fonctionnalités.

5 réflexions au sujet de « Espace de code sécurisé »

  1. Merci pour cet article qui regroupe de manière condensé des bonnes pratiques.
    Il y a beaucoup de choses très intéressantes mais j’ai toujours le même problème : j’ai beaucoup de mal à me tenir à ces nombreuses conventions assez strictes…
    Lorsque je code en JS, je pense à certaines bonnes pratiques et pas à d’autres, et c’est pas toujours les mêmes auxquelles je pense ou pas… Du coup c’est très bordélique.
    J’ai pas encore trouvé de « bonne » solution pour gérer ces « problèmes d’attention »… Mais à force, ça devrait finir par rentrer (après avoir pondu je ne sais combien de lignes un peu bancales, malheureusement…)

  2. Que de bonnes pratique.
    Des classiques, mais qu’il est toujours bon de revoir.

    Pour le ‘use strict’ par contre ça à été une découverte. Je vais approfondir. Merci

    J’ai beaucoup aimé ton intro. Et ta manière de présenter les chose.

  3. Merci pour ces encouragements qui font bien plaisir! :-D

    1. IIFE
    J’aurais dû préciser que les parenthèses qui entouraient la déclaration des fonctions auto-appelantes permettaient de transformer une instruction en expression, seule cette dernière étant habilitée à être appelée avec les parenthèses qui suivent. De telles parenthèses peuvent s’appliquer partout, et il ne s’agit pas vraiment d’une spécificité des fonctions IIFE. Par exemple (4).toFixed(3);

    Autre point, certains passent également la variable undefined parmi les paramètres sans la passer par les arguments. En effet, undefined est accessible en écriture et peut donc être écrasé…

    2. Mode strict
    Le mode strict va bientôt réapparaître dans un tout prochain article.

    3. Namespace
    Je ne pense pas que l’utilisation des namespaces telle qu’exposée dans mon article soit totalement optimale, pour les raisons suivantes:
    - window n’est pas une référence absolue, puisque cette variable dépend de l’environnement d’exécution du script. Ainsi, dans node.js, par exemple, window n’existe pas. Ceci rend le code peu portable.
    - Il existe un pattern extrêmement puissant qui gère ce problème bien mieux et que je viens de découvrir. Il s’agit de la Loose Augmentation.

    Génial non?

  4. Ping : Espace de code sécurisé | The Dark Side Of The Web | Bonnes Pratiques Web | Scoop.it

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Vous pouvez utiliser ces balises et attributs HTML : <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>