Lade...
 

MorphIT Assetrouting

MorphIT Assetrouting

4.4.0

Das Assetrouting von MorphIT wurde mit Version 4.4.0 grundlegend überarbeitet. Die Pfadkonfiguration wurde so gestaltet, dass die bisherige Konfiguration ohne Anpassung weiter verwendet werden kann. Will man jedoch eines der neuen Features nutzen, dann muss sie auf die neue Schreibweise umgeschrieben werden.

Features

public/private Assets

Die neue Pfadkonfiguration erlaubt es, die Assetpfade in public und private Assets zu unterteilen. Die frühere MorphIT-Version kennt nur public Assets. Dies sind die Dateien, die der MorphIT-Server ohne Authentifzierung an jeden HTTP-Client ausliefert, der die korrekte URL kennt. Die URL enthält den Pfad zur ausgelieferten Datei im Klartext, was auch heißt, dass man versuchen kann, gültige Pfade zu erraten, auch wenn ClassiX den Pfad nicht ausgeliefert hat. (Bsp.: Wenn es /public/icons/bild_2.bmp gibt, dann gibt es wahrscheinlich auch /public/icons/bild_1.bmp)

Private Assets sind in zweierlei Hinsicht geschützt. Erstens benötigt der HTTP-Client für den Zugriff auf diese Assets ein Cookie, welches zugewiesen wird, sobald eine Websocket-Verbindung mit der ClassiX-Instanz aufgebaut wurde, also nach dem Login. Zweitens sind die Pfade mit einem Schlüssel verschlüsselt, den nur die herausgebende ClassiX-Instanz und der MorphIT-Server kennen. Es ist also unmöglich, auch bei bekanntem Klartext aus einem verschlüsselten privaten Pfad (Bsp: /private/assets/zgEAAADxJKgk3G86gwAtR5tKX9nKWP3RpEeqG74adFGt3gtE8A)  einen anderen gültigen, privaten Pfad zu erraten, oder Informationen aus der Pfadstruktur abzuleiten.

Alle Assetpfade folgen der Struktur: /Kategorie/Id/Pfad
Beispiele:

URL Kategorie Id Pfad
/private/assets/zgEAAADxJKgk3G86gwAtR5tK... private assets zgEAAADxJKgk3G86gwAtR5tK...
/public/icons/bild_2.bmp public icons bild_2.bmp
/public/icons/myapp/custom/image.png public icons myapp/custom/image.png

 

Schlüsselaustausch und Verschlüsselung

Der MorphIT-Server generiert für jede ClassiX-Instanz einen zufälligen 128 Bit Schlüssel, der bei der Registrierung der ClassiX-Instanz mitgeteilt wird. Sobald ein MorphIT-Client mit dieser ClassiX-Instanz verbunden wird, erhält er ein Cookie (zufälliger SHA-256 Hash), zu dem (beim Server) hinterlegt ist, mit welchen ClassiX-Instanzen der Client verbunden ist, sodass er mit diesem Cookie auch nur die Pfade entschlüsseln kann, die seine eigenen ClassiX-Instanzen generiert haben. Will eine solche Instanz nun einen Pfad zu einem privaten Asset herausgeben, dann wird der Pfad nach folgendem Schema verschlüsselt:

base64url(<xor-checksum:uint8>,<cx-id:uint32-le>,aes-128-cbc((<path>,';',<path.length%256:uint8>),<key>))

Der Pfad wird vor der Verschlüsselung um das Zeichen ; und die Bytelänge des Pfads modulo 256 ergänzt. Diese beiden Werte werden im Server bei der Entschlüsselung überprüft, um sicherzustellen, dass der Pfad mit dem korrekten Schlüssel entschlüsselt wurde. Diese Prüfung ist notwendig, da der Teil, der den zu verwendenden Schlüssel bestimmt selbst nicht verschlüsselt ist und von einem Angreifer manipuliert werden könnte.

Der so ergänzte Pfad wird nun mit dem ausgetauschten Schlüssel und AES im Cipher Block Chaining (CBC)-Modus verschlüsselt. Als Initalisierungsvektor (IV) für CBC wird für jeden Pfad ein NULL-Block (1 Block = 8 Bytes) verwendet. AES wurde gewählt, weil hierfür im Gegensatz zu einfacheren Verschlüsselungsschemata (wie XOR) ein Known-Plaintext-Angriff nicht verwendet werden kann, um Informationen über den Schlüssel zu erhalten. Der CBC-Modus wird verwendet, um zu verhindern, dass verschlüsselte Blöcke zwischen zwei Pfaden ausgetauscht werden können, um wieder einen gültigen, verschlüsselten Pfad zu erzeugen (was bei ECB möglich wäre). Sobald sich ein Klartextblock im CBC-Mode von einem anderen Pfad unterscheidet, unterscheiden sich auch alle nachfolgenden Blöcke. 

Es wird in Richtlinien grundsätzlich davon abgeraten, einen IV zu benutzten, der bekannt und für alle zu verschlüsselten Nachrichten gleich ist. Dies trotzdem so zu implementieren hat die folgende Konsequenz: Wenn die ersten n Blöcke zweier Pfade identisch sind, dann sind auch die ersten n verschlüsselten Blöcke identisch. Ein Angreifer kann somit erkennen, ob zwei Pfade den gleichen Präfix haben. Dieses Problem wird von uns bei der Verschlüsselung von Pfaden als unkritisch angesehen, da gleiche Präfixe in Pfaden keine sicherheitsrelevanten Informationen sind.

Dies zu verhindern hieße für jeden privaten Pfad einen zufällig generierten IV zu verwenden, der dann auch in diesen Pfad hineinkodiert werden müsste und den Pfad somit um 11 Klartextbytes (8 Bytes -> 11 Bytes in Base64) verlängern würde. Da der IV im Pfad steht und somit unter der Kontrolle des Angreifers ist, könnte der Angreifer bei Pfaden, die nur aus einem Block bestehen und deren Klartext er kennt, durch Manipulation des IV im Pfad beliebige andere Pfade (mit der Blocklänge 1) generieren, die ebenfalls als gültig akzeptiert werden und damit könnte er bei sehr kurzen Pfaden und Kenntnis eines Pfades frei navigieren, was die Verschlüsselung ursprünglich verhindern sollte. Aufgrund des zusätzlichen Aufwands und der faktisch geringeren Sicherheit, wird der konstante IV gewählt, der vom Angreifer nicht manipuliert werden kann.

 

Dem verschlüsselten Pfad wird anschließend die ID der ClassiX-Instanz vorangestellt sowie eine 1-Byte XOR-Prüfsumme der gesamten Nachricht. Die ClassiX-ID wird in den Pfad kodiert, weil jede ClassiX-Instanz einen anderen Schlüssel verwendet und so der Server die Zuordnung zwischen Pfad und Schlüssel vornehmen kann. Außerdem kann der Server so prüfen, ob das übermittelte Cookie mit dieser ClassiX-Instanz verbunden ist. Diese Information muss im Pfad hinterlegt  werden und nicht im Cookie, weil die Anwendung auch in mehreren Browser-Tabs angemeldet sein kann und sich alle Browser-Tabs ihre Cookies teilen. Eine eindeutige Zuordnung von Cookie → ClassiX ist somit nicht möglich. Die XOR-Prüfsumme soll sicherstellen, dass die Base64-Kodierung des Pfads korrekt ist und ungültige Pfade möglichst vor der Entschlüsselung erkannt werden.

Dieser binäre, verschlüsselte Pfad wird abschließend mit base64url kodiert, wobei die Padding-Bytes weggelassen werden.

 

Der Server liefert sowohl für gültige Pfade, deren Datei nicht gefunden wurde, als auch für ungültige Pfade die gleiche Fehlermeldung, sodass ein Padding Oracle Angriff nicht verwendet werden kann, um die AES-Verschlüsselung ohne Kenntnis des Schlüssels aufzuheben. Fehlerursachen meldet der Server nur, wenn die zur Entwicklung vorgesehenen Optionen dev.asset_errors und dev.server_errors in der Server-Konfiguration gesetzt sind.

Cookies?
Das Cookie, welches für den Zugriff auf die privaten Assets benötigt wird, wird erst nach dem Login gesetzt. Cookies sind der einzige vom Browser unterstützte Mechanismus, um einen HTTP-Client über mehrere Anfragen hinweg zu authentifizieren.
Sollten keine privaten Assets im MorphIT-Server konfiguriert sein, dann wird wie bisher auch nach dem Login kein Cookie gesetzt.

 

Private Assets aus private Assets heraus referenzieren

Die Pfade von private Assets werden vollständig verschlüsselt an den Browser herausgegeben. Hierdurch kann der Browser keine relative Navigation durchführen, was für private Assets auch so gewollt ist (Bsp: /private/assets/zgEAAADxJKgk3G86gwAtR5tKX9nKWP3RpEeqG74adFGt3gtE8A/../style.css ist kein gültiger Pfad). Da für das Generieren eines korrekten private Pfads sowohl die ClassiX-ID als auch der Schlüssel dieser Instanz notwendig ist, können statische Assets (z.B. eine .html-Datei) keine gültigen private Pfade enthalten. Um dennoch zu ermöglichen, dass zum Beispiel ein private .html-Asset ein private .css-Asset referenzieren kann, werden vordefinierte Dateitypen (.html, .css & .js) vom Server interpretiert, ehe sie zum Client geschickt werden. Dabei werden EJS ähnliche Tags innerhalb der Dateien ausgewertet.

Beispiel für eine .html-Datei, die Bild-Dateien referenziert.

Privateassetejs

Diese speziellen Tags werden nur innerhalb von private Assets interpretiert. Da der Link auf ein private Asset nur von einer ClassiX-Instanz herausgegeben worden sein kann und der Link die ID der ClassiX-Instanz enthält, weiß der Server an der Stelle sicher, dass der Client, der das private Asset anfragt, authentifiziert ist und welcher Schlüssel verwendet werden muss, um private Assets für die gleiche ClassiX-Instanz zu generieren. Da für public Assets beides nicht zutrifft, werden diese Tags innerhalb von public Assets nicht ausgewertet.

Innerhalb der Klammen <%= und =%> können die folgenden Funktionen verwendet werden, die die gesamte Klammer durch den entsprechenden Pfad ersetzen.
Die Argumente werden in einfachen Klammern übergeben und können optional in " " oder ' ' eingeschlossen sein.

Funktion Beschreibung
public Der übergebene Pfad sollte die Form Id/Pfad haben und wird lediglich um public/ erweitert. Sollte Id nicht im public-Bereich der Assetkonfiguration definiert sein, wird das Tag stattdessen zu INVALID_PUBLIC_OR_PRIVATE_PATH!!! ersetzt.
private Der übergebene Pfad sollte die Form Id/Pfad haben und wird um private/ erweitert und der Pfad wird verschlüsselt. Sollte Id nicht im private-Bereich der Assetkonfiguration definiert sein, wird das Tag stattdessen zu INVALID_PUBLIC_OR_PRIVATE_PATH!!! ersetzt.
relative Der übergebene Pfad sollte ein Dateisystempfad relativ zu der aktuellen Datei sein. Die referenzierte Zieldatei sollte in einem Verzeichnis liegen, das in der Assetkonfiguration entweder als public oder private Verzeichnis definiert wurde. Je nachdem welche Kategorie der Asset-Pfad hat, wird der Pfad als public-Pfad oder private-Pfad generiert. Sollte der angegebene Pfad nicht teil der Assetkonfiguration sein, dann wird das Tag durch INVALID_ABSOLUTE_OR_RELATIVE_PATH!!! ersetzt.
absolute Der übergebene Pfad sollte ein absoluter Dateisystempfad auf der Maschine sein, auf der die aktuelle Datei liegt. Die referenzierte Zieldatei sollte in einem Verzeichnis liegen, das in der Assetkonfiguration entweder als public oder private Verzeichnis definiert wurde. Je nachdem welche Kategorie der Asset-Pfad hat, wird der Pfad als public-Pfad oder private-Pfad generiert. Sollte der angegebene Pfad nicht teil der Assetkonfiguration sein, dann wird das Tag durch INVALID_ABSOLUTE_OR_RELATIVE_PATH!!! ersetzt.

Tags, die unbekannte oder syntaktisch falsche Funktionen verwenden, werden vom Server nicht interpretiert und unverändert herausgegeben.

 

Hinweis: Da private Assets, die vom Server interpretiert werden (.js, .css, .html), dynamische Inhalte darstellen, werden die Header E-Tag und Content-Length nicht vom Server gesetzt. Dies führt dazu, dass der Browser die Ressourcen nicht cachen kann und beim Download größerer Dateien keinen Fortschrittsbalken anzeigt.

local/remote Assets

Die neue Pfadkonfiguration ermöglicht es nun, dass der MorphIT-Server Assets von einer anderen Maschine aus ausliefert, solange dort ein MorphIT-Launcher-Prozess läuft, der mit dem Server verbunden ist. Bei jedem Assetpfad kann optional angegeben werden, auf welcher Maschine die Assets liegen. Falls eine Datei aus einem remote Pfad angefragt wird, leitet der Server die Anfrage an den Launcher weiter, der die Datei zum Server pushed, der diese wiederum zum Client weiterleitet.

RemoteAssets

Achtung: Die Dateianfrage wird über den bereits aufgebauten Websocket gestellt, aber die Datei selbst wird über eine neue HTTP-PUT-Anfrage übertragen. Diese Anfrage wird auf dem Port port oder (externalPort) aus der geladenen config.js gestartet. Hierfür ist es wichtig, dass diese Werte in der Serverkonfiguration und der Launcherkonfiguration übereinstimmen, damit die Datei korrekt übertragen werden kann.

 

statische/dynamische Pfadkonfiguration

Die statische Pfadkonfiguration wird in der config.js vorgenommen und ist im Abschnitt "Konfiguration" beschrieben. Die statische Pfadkonfiguration definiert für alle ClassiX-Instanzen die gleiche Pfadkonfiguration und ist für die meisten MorphIT-Anwendungen die richtige Wahl.

 

Die dynamische Pfadkonfiguration ist für komplexere Szenarien entwickelt worden und wird aktiviert, indem in der config.js cloud.enabled=true gesetzt wird. Damit wird der Server in den Cloud-Modus geschaltet und wertet bei Assetanfragen zusätzlich Query-Parameter aus, die in cloud.routing_parameters definiert werden (Default: "client", "product"). Die Idee des Cloud-Modus ist, dass dem MorphIT-Server ein Reverse-Proxy vorgeschaltet ist, der Pfade nach einem vordefinierten Schema umschreibt, und es so ermöglicht mehrere unterschiedliche MorphIT-Anwendungen mit einem MorphIT-Server zu betreiben. Diese Konfiguration wird in der classix.cloud verwendet. Der Pfad classix.cloud/ bleibt classix.cloud/, aber classix.cloud/direct/classix/qm/ wird zu classix.cloud/?client=classix&product=qm. Dieses Mapping betrifft auch alle Assets. Die dynamische Pfadkonfiguration dient nun dazu, dem Server mitzuteilen, wo die Assets für Pfade wie classix.cloud/public/icons/icon1.png?client=classix&product=qm liegen. Abhängig von den Query-Parametern liefert der Server so für gleiche Pfade unterschiedliche Assets aus.

Die dynamische Pfadkonfiguration wird über den Serverbefehl set_path_map gesetzt. Dies sollte am besten die Webservice-Instanz tun, sobald sie als Webservice-Instanz ausgewählt wurde. Die zu setzende Pathmap hat folgende Struktur:

{ "public": {...} "private": {...}, "classix": { "public":{...} "private":{...} "qm": { "public": {...}, "private": {...} }, "gestin": { "public": {...}, "private": {...} } }, "customer3": {...}, ... }

Die Anzahl der Ebenen ist durch die Anzahl der cloud.routing_parameters definiert und die Ebenen werden in der gleichen Reihenfolge definiert. Die erste Ebene ist bei der Standardkonfiguration client und die zweite ist product. Auf jeder Ebene können wie in der statischen Pfadkonfiguration die Pfade für die Kategorien public und private definiert werden. Die tieferen Ebenen "erben" die Pfaddefinitionen aus den darüber liegenden Ebenen, sodass die public/private-Definitionen auf der obersten Ebene für alle client/product-Kombinationen gelten.

Wird für eine in der Pfadkonfiguration nicht angegebene Kombination aus client/product ein Asset angefragt, dann wird die Pfadmap aus dem letzten bekannten Knoten generiert (falls client definiert ist, dann daraus, ansonsten aus dem public/private der obersten Ebene).

 

Wichtig: Auch bei aktiviertem Cloud-Mode wird für Anfragen ohne Query-Parameter die statische Pfadkonfiguration verwendet. Diese lässt sich auch nicht über set_path_map überschreiben und wird bei Anfragen mit Query-Parameter nicht berücksichtigt. Die dynamische und statische Konfiguration werden nicht gemerged.

Hinweis: ab MorphIT-Version 4.11.3 kann die Option cloud.apply_static_path_map gesetzt werden, wodurch die in der statischen Pfadkonfiguration definierten Pfade immer gelten. set_path_map kann in diesem Fall die Pfade für bestimmte Query-Parameter nur additiv erweitern.

Wenn die dynamische Pfadkonfiguration verwendet wird, dann muss eine zu startende ClassiX-Instanz auch darüber informiert werden, welche Pfadkonfiguration von ihr verwendet werden soll, damit absolute Pfade korrekt in relative URLs umgerechnet werden können. Diese Information muss dem Server bei request_binding im asset_route-Parameter mitgeteilt werden. Die entsprechende Pfadkonfiguration wird der gestarteten ClassiX-Instanz nach der Registrierung vom Server mitgeteilt.

ClassiX-Instanzen, die vom Launcher automatisch vorgestartet werden, erhalten immer die statische Pfadkonfiguration vom Server.

Konfiguration

Die statische Assetkonfiguration wird in der config.js vorgenommen. Hierfür gibt es den neuen Schlüssel paths. Der Default-Wert auto definiert eine Pfadkonfiguration, die äquivalent zu der bisherigen ist und sich auf die Variablen iconPath, bmpPath, widgetPath und assetsPath bezieht.

Die generierte Konfiguration sieht wie folgt aus:

'paths': { 'public':{ 'icons': {'path':'${iconPath}', 'alias':'/assets/images/icons'}, 'bmp': {'path':'${bmpPath}', 'alias':'/assets/images/bmp'}, 'widgets': {'path':'${widgetPath}', 'alias':'/widgets'}, 'assets': {'path':'${assetsPath}', 'alias':['/assets/images/Projects/Data/Assets','/Projects/Data/Assets'], 'cache':false}, }, 'private': false // could be {...} to define and enable private paths }
Wichtig: Jede Pfadkonfiguration muss den Pfad /public/widgets/ definieren, ansonsten können WebWidgets nicht verwendet werden.

Struktur

paths

Feld Typ Beschreibung
public Objekt category-Objekt, welches die public Pfade definiert.
private Objekt | false category-Objekt, welches die private Pfade definiert oder false falls private Pfade deaktiviert sein sollen.

category

Feld Typ Beschreibung
id Objekt

Ein pathdefinition-Objekt, welches für die angegebene id definiert, woher und wie der Server die Dateien ausliefern soll.
Bis auf widgets hat die id keine besondere Semantik und ist frei wählbar. Es ist auch erlaubt die gleiche id in unterschiedlichen Kategorien zu verwenden.

/public/widgets/ muss die Quellen der auszuliefernden WebWidgets enthalten. (Bisher: die Config-Variable widgetPath)

pathdefinition

Feld Typ Beschreibung
path string | Array Der absolute Pfad zum Dateisystemverzeichnis, aus welchem die Dateien ausgeliefert werden sollen.
Werden hier in einem Array mehrere Pfade angegeben, dann werden alle angegebenen Verzeichnisse nach der Datei durchsucht.
host string Die Maschine auf der die Dateien liegen (siehe: remote Assets). Die Maschine kann per Hostname oder IP-Adresse angegeben werden. Wenn es sich bei der Maschine nicht um die lokale Maschine des Servers handelt, muss dort ein Launcher-Prozess laufen, der die Dateien ausliefert. Diese Angabe ist optional und der Default ist localhost.
alias string | Array Hierüber können ein oder mehrere Alias-Pfade definiert werden, über die der Assetpfad ebenfalls erreichbar ist. Dieses Feld ist nur für public Assets sinnvoll und hautsächlich für die Migration gedacht. Wird beispielsweise /alias als Alias definiert, dann sind die Assets zusätzlich zu /public/id auch unter /alias erreichbar. Die Angabe ist optional und und ohne Angabe werden keine Alias-Pfade definiert.
cache bool Kann auf false gesetzt werden, um zu verhindern, dass der Browser die Assets cached. Der Server setzt in dem Fall HTTP-Header, die den Browser anweisen, die Dateien nicht zu cachen. Die Angabe ist optional und der Default ist true.