
Ein Pythonisches Framework zur Bedrohungsmodellierung
Traditionelle Bedrohungsmodellierung kommt oft zu spät auf die Party – oder manchmal gar nicht. Darüber hinaus kann das manuelle Erstellen von Datenflüssen und Berichten extrem zeitaufwändig sein. Das Ziel von pytm ist es, die Bedrohungsmodellierung nach links zu verschieben, sie automatisierter und entwicklerzentrierter zu gestalten.
Basierend auf deiner Eingabe und der Definition des Architekturentwurfs kann pytm automatisch die folgenden Elemente generieren:
Die tm.py ist ein Beispielmodell. Du kannst es ausführen, um den Bericht und die darin referenzierten Diagrammbilddateien zu erzeugen:```
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
Es gibt auch ein Beispiel-`Makefile`, das all diese in Ziele zusammenfasst, die leicht für mehrere Modelle gemeinsam genutzt werden können. Wenn Sie [GNU make](https://www.gnu.org/software/make/) installiert haben (standardmäßig auf Linux-Distributionen verfügbar, aber nicht auf OSX), führen Sie einfach aus:```
make MODEL=the_name_of_your_model_minus_.py
Sie sollten entweder plantuml.jar im selben Verzeichnis wie Ihr Modell haben oder PLANTUML_PATH setzen.
Um die Installation aller Abhängigkeiten wie pandoc oder Java zu vermeiden, kann das Skript in einem Container ausgeführt werden:```
export USE_DOCKER=true make image
make
### Erste Schritte - Devbox-Variante
Um die Nutzung von `pytm` zu vereinfachen, können Host-Abhängigkeiten vollständig isoliert werden, indem [`Devbox`](https://github.com/jetify-com/devbox) verwendet wird. Dies ist in der Regel eine weniger aufwändige und bequemere Alternative zum OCI-Container-Ansatz.
- Devbox unter Linux/MacOS installieren: `curl -fsSL https://get.jetify.com/devbox | bash`
- Devbox unter [Windows/WSL](https://www.jetify.com/docs/devbox/installing-devbox/index#installing-wsl2) installieren
- Auf die neueste Version von devbox aktualisieren: `devbox version update`
- Setzen Sie Ihren GitHub-Zugriffstoken in der Datei `~/.config/nix/nix.conf`: `access-tokens = github.com=YOUR_TOKEN_HERE`
- Erstellen Sie eine neue, isolierte Shell-Umgebung, die alle im Projekt `devbox.json` angegebenen Tools und Pakete enthält: `devbox shell`
- Zeigen Sie den vollständigen Pfad zur Python-Ausführungsdatei an, die verwendet wird, wenn Sie einfach `python` in Ihrem Terminal eingeben, indem Sie den Befehl `which python` verwenden. Die Ausgabe sollte der folgende Pfad sein: `.devbox/nix/profile/default/bin/python`
- Testen Sie, indem Sie den folgenden Befehl ausführen, der ein DFD als PNG-Datei namens `sample.png` generieren sollte: `./tm.py --dfd | dot -Tpng -o sample.png`
- Die Devbox-Shell-Umgebung verlassen: `exit`
## Verwendung
Alle verfügbaren Argumente:```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
Der stale_days Parameter versucht zu ermitteln, wie weit das Modell-Skript (das Sie schreiben) in Tagen von dem Code entfernt ist, der das modellierte System implementiert. Idealerweise sollten diese in den meisten Fällen eines aktiv entwickelten Systems recht nahe beieinander liegen. Sie können dies regelmäßig ausführen, um den Puls Ihres Projekts und die ‚Frische‘ Ihres Bedrohungsmodells zu messen.
Derzeit verfügbare Elemente sind: TM, Element, Server, ExternalEntity, Datastore, Actor, Process, SetOfProcesses, Dataflow, Boundary, Lambda, LLM und Agent.
Die verfügbaren Eigenschaften eines Elements können mit --describe gefolgt vom Namen eines Elements aufgelistet werden:```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
Das *colormap*-Argument, zusammen mit *dfd* verwendet, gibt ein farbcodiertes DFD aus, bei dem die Elemente je nach Risikostufe (ermittelt durch die Ausführung der Regeln) rot, gelb oder grün eingefärbt werden.
## Nutzung - Devbox-Variante
- `devbox shell`
- `pytm` Nutzung wie gewohnt
- `exit`
## Erstellen eines Bedrohungsmodells
Im Folgenden finden Sie eine Beispiel-`tm.py`-Datei, die eine einfache Anwendung beschreibt, bei der sich ein Benutzer in die Anwendung einloggt und Kommentare in der App postet. Der App-Server speichert diese Kommentare in der Datenbank. Es gibt eine AWS Lambda, die regelmäßig die Datenbank bereinigt.```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
)