Threat modelling avec Archimate

Le point de départ

En lisant - on a les occupations qu'on peut - le livre de préparation de la certification CISSP (# (ISC)2 CISSP Certified Information Systems Security Professional Official Study Guide, 9th Edition – ISBN-10: 1119786231), je suis tombé sur une page concernant le threat modelling.

L'idée est de représenter visuellement un certain nombre de composants d'une solution pour faciliter l'identification des menaces. Exemple :

/img/2023/threat00.png

Source :

Il s'agit d'une forme de représentation graphique que je n'avais jamais croisée dans le monde professionnel. Y sont notamment indiqués :

Je n'ai pas trouvé de formalisme bien défini pour ce type de visualisation et me suis demandé si Archimate pouvait répondre à ce type de besoin en apportant un peu plus de « cadre » à la représentation.

_Cet article présuppose que vous connaissez Archimate ainsi que le logiciel

Le schéma

Première étape, une proposition de mapping entre les concepts utilisés pour ce threat modelling et les concepts Archimate :

Eléments du threat model => Concept Archimate

La difficulté principale est, à mon sens, la modélisation des « frontières », Archimate n'ayant pas de concept de « limite ». Plusieurs solutions sont envisageables

/img/2023/threat01.png

La dernière approche a ma préférence car elle évite d'introduire un nouveau concept pour représenter la « boundary » tout en permettant de réutiliser des concepts qui ont probablement déjà été introduits par ailleurs dans le modèle.

Voilà un exemple basique pour une application web avec load balancer, serveurs frontends mutualisés à la maille de l'entreprise, puis frontends et backends dédiés à chaque application. Les backends sont dans une zone sécurisé, tout le reste est dans une zone réseau « à plat ». On a donc 2 « boundaries » :

Enfin, on identifie 3 vulnérabilités (en violet), nous y reviendrons dans le § suivant : 2 sont associées à des flux non chiffrés, 1 à l'absence de filtrage sur un frontend.

/img/2023/threat02.png

Pour aller plus loin

Nous avons maintenant produit un visuel, c'est bien, cela permet de communiquer, de clarifier les concepts. Tout l'intérêt de modéliser n'est pas juste de produire un beau schéma, mais de progressivement bâtir un modèle qu'on pourra ensuite valoriser par des analyses, des requêtes. Mon objectif est ici, une fois une vulnérabilité modélisée, de « scanner » le reste du modèle afin de détecter tous les autres cas où cette vulnérabilité se rencontre.

Sur l'exemple, ci-dessus, on modélise 3 vulnérabilités :

Pour cela, nous avons besoin de créer des règles. Elles vont être implémentées via une propriété de la vulnérabilité, ici notée « check_rule » :

/img/2023/threat03.png

Elles sont écrites en javascript afin d'être évaluées par le plugin jArchi d'Archi. Par convention, on considère que la variable « o » référence le concept associé à la vulnérabilité (un flow-relationship ici).

La première vulnérabilité « Unencrypted flow » se code :

o.name.indexOf("(http)")>-1

Se traduit par, « il y a vulnérabilité si le nom du flux indique que le protocole est (http) ».

La vulnérabilité « Unencrypted flow between 2 different zones » peut se coder de la façon suivante :

((o.name.toLowerCase().indexOf("(http)")>-1) && (!compareFirstAncestors(o.source,o.target,"location")))

En bon français, cela se lit : on a une vulnérabilité si le flux est indiqué comme (http) ET que la source et la destination du flux sont situés dans des « locations » différentes.

La méthode compareFirstAncestors n'existe pas dans le SDK de jArchi, c'est une méthode que j'ai conçue pour l'exemple et qui compare le premier « parent » de type « location » contenant chacun des 2 concepts passés en paramètre de la méthode. Elle retourne true si c'est le même concept.

Enfin, la vulnérabilité « No filtering on incoming request » décrite de manière ultra-simplifiée par :

(o.name.indexOf("Frontend")>-1) && (!isRelatedTo(o, "filtering","association-relationship"))

Traduction : il y a vulnérabilité si le composant contient le terme « Frontend » et n'est pas lié à un bloc nommé « filtering ».

De même que « compareFirstAncestors », la méthode « isRelatedTo » a été créée spécifiquement pour mon besoin.

Reste à rédiger ensuite le script qui va vérifier ces règles sur l'ensemble du modèle.

Le code suivant implémente ceci de manière très basique, il mérite un peu de travail pour être pleinement opérationnel :

    $("assessment").each(function (as) {
        let rule = null;
        if (as.prop("check_rule") !== undefined) {
            rule = as.prop("check_rule");
            let assessedElement = "";


            //get the type of the concept associated with the assessment (suppose that an assessment is always associated with this type of concept across models)

            $(as).rels("association-relationship").ends().each(function (element) {
                if (element.type !== "assessment") {
                    assessedElement = element.type;
                    return;
                }
            })

            //check for all concepts of this type in the model
            $(assessedElement).each(function (o) {
                //if rule evaluation is true, create a vulnerabilty association between assessment and object
                if (eval(rule)) {
                    if ($(as).rels("association-relationship").ends(o.type).filter("#" + o.id).size() === 0) {
                        model.createRelationship("association-relationship", "vulnerability", as, o)
                    } else {
                       //do nothing at the moment
                    }
                } else {
                    //delete relationship
                    $("association-relationship").each(function (rel) {
                        if ((rel.source.id === as.id)&& (rel.target.id===o.id) && rel.name==="vulnerability") {
                            rel.delete();
                        }
                    });
                }
            })

        }

    });

Notez la ligne « eval(rule) » qui comme son nom l'indique évalue (cad exécute) la ligne de code indiquée par la propriété « check_rule » de l'assessment.

Après avoir passé le script (et, pour les raisons du test modifiés le protocole du flux browser > load balancer en http), 2 nouvelles associations sont apparues dans le modèle (mais pas dans la vue, on pourrait toutefois facilement les faire ajouter dans le script)

Il y a donc nécessité de faire un peu de code et, surtout, de fournir un effort de formalisation des vulnérabilités. En contrepartie, cet exemple permet de discerner les apports d'un modèle, apports qui vont bien au-delà de simple représentation visuelle.

Conclusion

Ainsi, au-delà d'une « simple » représentation graphique via Archimate (ce qui est déjà beaucoup, entendons-nous bien), on peut utiliser la puissance du modèle pour capitaliser les vulnérabilités détectées sur une solution et rechercher leur occurrence dans les autres solutions modélisées.



Permalink :
https://www.jmus.fr/2023-09-10-threat-modelling.html