
Un framework Pythonic pour la modélisation des menaces
La modélisation traditionnelle des menaces arrive trop souvent en retard, ou parfois pas du tout. De plus, créer des flux de données et des rapports manuellement peut être extrêmement chronophage. L'objectif de pytm est de déplacer la modélisation des menaces vers la gauche, en la rendant plus automatisée et centrée sur le développeur.
En fonction de votre entrée et de la définition de la conception architecturale, pytm peut générer automatiquement les éléments suivants :
Le fichier tm.py est un modèle exemple. Vous pouvez l'exécuter pour générer le rapport et les fichiers d'images de diagramme auxquels il fait référence :```
mkdir -p tm
./tm.py --report docs/basic_template.md | pandoc -f markdown -t html > tm/report.html
./tm.py --dfd | dot -Tpng -o tm/dfd.png
./tm.py --seq | java -Djava.awt.headless=true -jar $PLANTUML_PATH -tpng -pipe > tm/seq.png
Il y a aussi un exemple de `Makefile` qui regroupe tout cela en cibles pouvant être facilement partagées pour plusieurs modèles. Si vous avez [GNU make](https://www.gnu.org/software/make/) installé (disponible par défaut sur les distributions Linux mais pas sur OSX), exécutez simplement :```
make MODEL=the_name_of_your_model_minus_.py
Vous devez soit avoir plantuml.jar dans le même répertoire que votre modèle, soit définir PLANTUML_PATH. Pour éviter d'installer toutes les dépendances, comme pandoc ou Java, le script peut être exécuté dans un conteneur :```
export USE_DOCKER=true make image
make
### Prise en main - Variante Devbox
Pour simplifier l'utilisation de `pytm`, les dépendances de l'hôte peuvent être complètement isolées en utilisant [`Devbox`](https://github.com/jetify-com/devbox). C'est généralement une alternative moins lourde et plus pratique que l'approche par conteneur OCI.
- Installer Devbox sur Linux/MacOS : `curl -fsSL https://get.jetify.com/devbox | bash`
- Installer Devbox sur [Windows/WSL](https://www.jetify.com/docs/devbox/installing-devbox/index#installing-wsl2)
- Mettre à jour vers la dernière version de devbox : `devbox version update`
- Définir votre jeton d'accès GitHub dans le fichier `~/.config/nix/nix.conf` : `access-tokens = github.com=YOUR_TOKEN_HERE`
- Créer un nouvel environnement shell isolé qui inclut tous les outils et paquets spécifiés dans le fichier `devbox.json` du projet : `devbox shell`
- Afficher le chemin complet vers l'exécutable Python qui sera utilisé lorsque vous tapez simplement `python` dans votre terminal en utilisant la commande `which python`. La sortie doit être le chemin suivant : `.devbox/nix/profile/default/bin/python`
- Tester en exécutant la commande suivante, qui devrait générer un DFD sous forme de fichier PNG appelé `sample.png` : `./tm.py --dfd | dot -Tpng -o sample.png`
- Quitter l'environnement shell Devbox : `exit`
## Utilisation
Tous les arguments disponibles :```text
usage: tm.py [-h] [--debug] [--dfd] [--report REPORT]
[--exclude EXCLUDE] [--seq] [--list] [--describe DESCRIBE]
[--list-elements] [--json JSON] [--levels LEVELS [LEVELS ...]]
[--stale_days STALE_DAYS]
optional arguments:
-h, --help show this help message and exit
--debug print debug messages
--dfd output DFD
--report REPORT output report using the named template file (sample
template file is under docs/template.md)
--exclude EXCLUDE specify threat IDs to be ignored
--seq output sequential diagram
--list list all available threats
--colormap color the risk in the diagram
--describe DESCRIBE describe the properties available for a given element
--list-elements list all elements which can be part of a threat model
--json JSON output a JSON file
--levels LEVELS [LEVELS ...]
Select levels to be drawn in the threat model (int
separated by comma).
--stale_days STALE_DAYS
checks if the delta between the TM script and the code
described by it is bigger than the specified value in
days
L'argument stale_days tente de déterminer l'écart en jours entre le script de modèle (que vous écrivez) et le code qui implémente le système modélisé. Idéalement, ils devraient être assez proches dans la plupart des cas d'un système en développement actif. Vous pouvez exécuter cela périodiquement pour mesurer le pouls de votre projet et la 'fraîcheur' de votre modèle de menace.
Les éléments actuellement disponibles sont : TM, Element, Server, ExternalEntity, Datastore, Actor, Process, SetOfProcesses, Dataflow, Boundary, Lambda, LLM et Agent.
Les propriétés disponibles d'un élément peuvent être listées en utilisant --describe suivi du nom d'un élément :```text
(pytm) ➜ pytm git:(master) ✗ ./tm.py --describe Element Element class attributes: OS definesConnectionTimeout default: False description handlesResources default: False implementsAuthenticationScheme default: False implementsNonce default: False inBoundary inScope Is the element in scope of the threat model, default: True isAdmin default: False isHardened default: False name required onAWS default: False
L'argument *colormap*, utilisé avec *dfd*, produit un DFD codé en couleur où les éléments sont colorés en rouge, jaune ou vert en fonction de leur niveau de risque (identifié par l'exécution des règles).
## Usage - Devbox Variant
- `devbox shell`
- utilisation de `pytm` comme d'habitude
- `exit`
## Creating a Threat Model
Voici un exemple de fichier `tm.py` qui décrit une application simple où un utilisateur se connecte à l'application et publie des commentaires sur l'app. Le serveur de l'application stocke ces commentaires dans la base de données. Il y a une AWS Lambda qui nettoie périodiquement la base de données.```python
#!/usr/bin/env python3
from pytm import TM, Server, Datastore, Dataflow, Boundary, Actor, Lambda, LLM, Data, Classification
tm = TM("my test tm")
tm.description = "another test tm"
tm.isOrdered = True
User_Web = Boundary("User/Web")
Web_DB = Boundary("Web/DB")
user = Actor("User")
user.inBoundary = User_Web
web = Server("Web Server")
web.OS = "CloudOS"
web.isHardened = True
web.sourceCode = "server/web.cc"
db = Datastore("SQL Database (*)")
db.OS = "CentOS"
db.isHardened = False
db.inBoundary = Web_DB
db.isSql = True
db.inScope = False
db.sourceCode = "model/schema.sql"
comments = Data(
name="Comments",
description="Comments in HTML or Markdown",
classification=Classification.PUBLIC,
isPII=False,
isCredentials=False,
# credentialsLife=Lifetime.LONG,
isStored=True,
isSourceEncryptedAtRest=False,
isDestEncryptedAtRest=True
)