Guten Tag zusammen. Mein Name ist Nathan Clark. Ich arbeite seit 22 Jahren mit JMP und bin Mitarbeiter von IDEXX Laboratories. Gemeinsam mit meiner Co-Referentin Sage Darling werde ich einen Vortrag zum Thema Geheimnismanagement halten.
Hallo. Ich bin Sage. Ich arbeite ebenfalls bei IDEXX, zusammen mit Nathan. Ich bin Senior Analytics Engineer und wir werden heute über Geheimnisse sprechen.
Ausgezeichnet. Sprechen wir zunächst darüber, was ein Geheimnis eigentlich ist. Aus Programmierersicht ist ein Geheimnis typischerweise eine Art Zugangsberechtigung, die Sie benötigen, damit Ihr Code funktioniert und Sie auf bestimmte Ressourcen zugreifen können. Gleichzeitig sollten andere diese Berechtigung nicht kennen. Es ist vergleichbar mit Ihren Passwörtern für die Computeranmeldung. Sie müssen sie häufig verwenden, sollten sie aber nicht an Dritte weitergeben.
Geheimnisse sind Mechanismen, die verschiedene Plattformen entwickelt haben, um Zugangsdaten so zu speichern, dass sie nach ihrer Veröffentlichung nicht einsehbar sind. Dadurch wird die Nutzung im Hintergrund deutlich sicherer. JMP bietet diese Funktion nicht standardmäßig. Da wir jedoch vermehrt mit Code und Hintergrundprozessen in JSL arbeiten, war uns diese Möglichkeit wichtig, um unsere Arbeit mit anderen zu teilen und auf verschiedene Ressourcen zuzugreifen, ohne versehentlich unabsichtlich Zugriff auf alle relevanten Informationen zu gewähren.
In einer Diskussion, die ich vor einiger Zeit mit dem JMP-Support führte, erhielt ich dieses kleine Codefragment, das, glaube ich, von Evan McCorkle geschrieben wurde. Er hatte es entwickelt, um diese Geheimnisse zu verschleiern und etwas zu schaffen, das so existieren könnte, wie wir es hier sehen. Man kann das Skript dann verschlüsseln und anschließend verwenden.
Im Prinzip verwendet es eine Funktion, die die Geheimnisse enthält, aber ein zweites Geheimnis oder eine Art Zugriffsphrase benötigt. Diese Phrase ist bekannt und in allen Funktionen des verschlüsselten Codes enthalten, aber niemand sonst kennt sie. Selbst wenn jemand weiß, dass die Funktion „Geheimnis A“ aufgerufen werden soll, kann er ohne diese Phrase nicht direkt auf die Geheimnisse zugreifen. Nach der Verschlüsselung kann man sie als geheimes Skript einbinden und so unbemerkt auf die Informationen zugreifen. Bisher konnte der JMP-Support trotz aller Bemühungen beim Testen keine Möglichkeit finden, das Geheimnis offenzulegen. Auch ich konnte es mit meinen eigenen Tests nicht. Es scheint ein vielversprechendes Framework zu sein, um darauf aufzubauen, wie man Geheimnisse sicher speichern und gleichzeitig deren Nutzung für verschiedene Code-Aktivitäten ermöglichen kann.
Wir haben heute zwei Beispiele. Ich zeige Ihnen anhand eines Beispiels, wie wir das für Snowflake umgesetzt haben. Hier sehen Sie – Entschuldigung – die Snowflake-Verbindungsvorlage. Das ist im Grunde die von mir geschriebene Funktion, bei der alle wichtigen Details ausgeblendet sind.
Ich werde das hier etwas erweitern. Die Funktion `get_secrets` gibt an, wo die Geheimnisse gespeichert sind. Für unsere Zwecke benötige ich, so wie es aktuell konfiguriert ist, nur einen Benutzernamen und ein Passwort, um mich bei Snowflake anzumelden. Das Passwort-Array ist dafür relativ einfach zu handhaben.
Ich habe aber noch weitere Unterstützungsfunktionen. Die erste Funktion stellt eine Verbindung zur Datenbank her und gibt den neuen Datenkonnektor zurück. Dieser Datenkonnektor ist eine Zeichenkette, die Sie selbst zusammenstellen. Er verbindet sich mit allen unternehmensspezifischen Komponenten. Sie benötigen lediglich den Snowflake-Treiber. Dies ist der Name des Snowflake-Treibers, den Sie bei der Installation von der Snowflake-Website auf Ihrem Computer erhalten. So können Sie die ODBC-Verbindung herstellen. Ihre Datenbank ist unternehmensspezifisch. Die Server Ihres Unternehmens sind jedoch mit Snowflake verbunden usw. Das Passwort wird aus dem Array abgerufen, das wir hier haben. Es wird eingefügt, und dann können Sie die anderen Komponenten verbinden.
Die letzte Funktion geht noch einen Schritt weiter. Sie nimmt die Zeichenkette der auszuführenden Abfrage entgegen, stellt alle Verbindungen her und führt die Abfrage innerhalb dieser Funktion aus. Anschließend werden alle Tabellenskripte entfernt, die mit dieser Tabelle verknüpft sind. Ich werde im Beispiel erläutern, warum dies nützlich sein kann. Im Grunde handelt es sich um eine zusätzliche Schutzebene, die verhindert, dass Anmeldeinformationen versehentlich weitergegeben oder offengelegt werden.
Dazu habe ich hier ein sehr einfaches Beispiel. Im ersten Beispiel wird im Grunde eine einfache SQL-Abfrage ausgeführt, und ich verwende benutzerdefiniertes SQL, indem ich die String-Version der Abfrage einfüge. Wenn ich das ausführe, erhalte ich eine Tabelle von Snowflake. Sie sieht BigClass sehr ähnlich, weil sie es auch ist. Ich habe einfach eine Kopie davon in Snowflake hochgeladen. Dadurch erhalten Sie die Tabelle genau so, wie Sie sie benötigen. Sie enthält einige Quellinformationen. Das kann nützlich sein. Mir ist aufgefallen, dass dadurch Benutzername und Passwort maskiert werden, was ich auch erwartet habe. Ich habe jedoch festgestellt, dass bei meinen verschiedenen Tests in einigen Fällen – ob zufällig oder nicht – das Passwort nicht sichtbar war, die Benutzer-ID aber schon. Je nachdem, wie streng Sie diese Zugangsdaten überwachen müssen, habe ich die zusätzliche Funktion erstellt, die diese Informationen entfernt.
Wenn ich diesen Abschnitt hier auskommentiere und das Skript jetzt ausführe, wird mit dieser anderen Funktion, in die ich diese Abfrage eingebe, eine neue Tabelle erstellt. Das Ergebnis ist dasselbe. Nur enthält die Tabelle jetzt keine Tabellenskripte mehr. Das ist praktisch. Um die Funktionsweise genauer zu erklären: Wenn ich beispielsweise diese Tabelle löschen und nur das Sterndiagramm mit allen Spalten auswählen möchte, sollte das jetzt funktionieren und alle Daten importieren. Dasselbe gilt, wenn Sie Filterfunktionen hinzufügen möchten. Das können Sie hier tun. Es funktioniert genau wie Standard-SQL-Abfragen. Sie übergeben einfach die Zeichenkette und erhalten Ihre bereinigte Datentabelle. Das ist ein relativ einfaches Beispiel. Ich übergebe nun an Sage, der eine komplexere Version hat, die zeigt, wie das mit der Schichtung Ihrer Funktion zusammenarbeitet. Ich übergebe jetzt an Sage.
Fox Drive ist ein Online-Speicherort für Ordner, in dem man Informationen ablegen kann. Wir nutzen ihn für viele verschiedene Zwecke. Deshalb wollte ich zunächst herausfinden, wie ich mich bei Box authentifizieren kann, um Dateien lokal von Box auf meinen Computer zu übertragen. Dazu muss ich eine App mit Box Developers entwickeln, mit der ich mich authentifizieren kann. Diese App benötigt dann ein sogenanntes JWT (JSON Web Token). Mithilfe dieses JWT kann ich dann ein Zugriffstoken erhalten, mit dem ich auf Box zugreifen kann. Hier gestaltete sich meine Situation etwas komplexer, da ich zuvor noch nie mit JSON oder JSON Web Tokens (JWTs) gearbeitet hatte. Daher musste ich die Implementierung hier vornehmen.
Wie Nathan bereits beschrieben hat, ist rechts ein Beispiel meiner secrets.jsl zu sehen. Diese Datei ist momentan nicht verschlüsselt, sodass man ihren Inhalt einsehen kann. Sie beginnt mit einer Funktion zum Abrufen der Geheimnisse. Hier werden alle meine JWT-Informationen gespeichert. Das JWT enthält Informationen wie Client-ID, Client-Geheimnis und andere anwendungsspezifische Daten. Diese Daten speichere ich in einem assoziativen Array, um die Geheimnisse abzurufen. Um auf die einzelnen Elemente zuzugreifen, verwende ich weitere Funktionen, die anhand der Client-ID auf die Elemente innerhalb dieser assoziativen Arrays zugreifen.
Ich zeige Ihnen nur eines davon, denn wenn ich alle durchgehen würde, sähen Sie alle Teile des assoziativen Arrays, und das wäre wahrscheinlich langweilig. Dies ist ein Beispiel, wie man die Client-ID erhält. Innerhalb dieses Codes verwende ich die Geheimnisse, um dann die Geheimnisse zu erhalten, die meinen Box-Zugriffsschlüssel enthalten, der mir dann die Client-ID liefert. Auch hier gilt: Alles bleibt geheim, damit die Funktion darauf zugreifen kann, da die Daten verschlüsselt sind.
Die Besonderheit, die ich hinzufügen musste, betrifft das Abrufen des Box-JWT-Tokens. Dies erfordert etwas mehr Fingerspitzengefühl, da JWTs Assertions benötigen, um ein neues Zugriffstoken zu erhalten, welches eine Signaturkomponente enthält. Da es sich um eine Signaturkomponente handelt, gilt sie als sensibles Material. Meine secrets.jsl enthält Funktionen, die diese Informationen von Box weiterverarbeiten. In diesem Fall erstellt meine JWT-Funktion – ich werde sie hier kurz umschließen, um die Übersicht zu verbessern – die Assertion mit Box, um den Aufwand für den Abruf des Zugriffstokens zu generieren. Dazu benötige ich die oben genannten Informationen zu meinen Geheimnissen: meine Box-Unternehmens-ID, meine Box-Client-ID, meinen öffentlichen Box-Schlüssel, meinen privaten Schlüssel und meine für meine App eindeutige Paraphrase.
Von dort habe ich einiges zur Fehlerbehebung, zum Beispiel wenn Schlüssel fehlen – was wirklich wichtig ist. Um die JWT-Assertion durchzuführen, musste ich das tatsächlich mit Python machen. Das beinhaltet auch ein weiteres Beispiel für die Verwendung einer anderen Sprache innerhalb der secrets.jsl, was ebenfalls eine Herausforderung darstellte, um sicherzustellen, dass die Daten ausgegeben werden konnten. Deshalb befindet sich die Verschlüsselung in der secrets.jsl und nicht nur im Skript, mit dem ich die JWT-Informationen verwende. Das ist auch eine einzigartige Gelegenheit, die Integration von Python in JSL zu sehen, was ziemlich cool ist.
Ich sende diese Informationen dann an das Python-Skript, das daraufhin eine spezifische Signatur für das JWT abruft. Anschließend erstellt es die Anmeldeinformationen und gibt die JWT-Assertion zurück, die ich später verwenden kann. Mit dieser JWT-Assertion erhalte ich mein Zugriffstoken. Dazu erhalte ich mein Box-JWT-Token (siehe oben) und verwende meine Client-ID und mein Client-Geheimnis, um ein Token zu erstellen. Dazu verwende ich die JWT-Assertion, meine Client-ID und mein Client-Geheimnis und sende das Ergebnis per HTTP-Anfrage an Box. Anschließend erhalte ich mein neues Zugriffstoken.
Ich habe hier auch eine Art Filterfunktion eingebaut für diejenigen, die tiefer graben möchten. Dann sendet sie meine Informationen darüber, ob ich das Zugriffstoken tatsächlich erhalten habe oder nicht, und gibt das Zugriffstoken anschließend an den Benutzer zurück.
Wo wird das verwendet? Wie wird das verwendet? Ich zeige Ihnen mein Beispiel, da wir über Zugriffstoken selbst noch einige interessante Diskussionen führen können. Zur Integration habe ich einen generischen Ordner, den ich Ihnen beispielhaft zeigen werde. In meinem eigentlichen Code zeige ich Ihnen jedoch nur einige Eingaben.
Hier binde ich die Box-Verbindung ein, die in meinem Fall verschlüsselt ist, da Sie dies nach der Bearbeitung der geheimen Datei tun sollten. Box verwendet ein System zur Ordnerstruktur mit ID-Codes, um den jeweiligen Ordner für die Dateisuche eindeutig zu identifizieren. Hier tragen Sie die eindeutige Ordner-ID ein. Ich rufe in diesem Beispiel nur JMP-Dateien ab. Je nach Ihren Anforderungen kann dies deutlich komplexer sein, aber mein Beispiel beschränkte sich auf JMP-Dateien.
Ich habe hier auch einen Parameter hinzugefügt, mit dem ich die Rekursionstiefe überprüfen kann. Box ist interessant, weil es objektorientiert arbeitet. Man hat eine Ordner-ID und darin wiederum Unterordner-IDs, weshalb die Ordner rekursiv durchsucht werden müssen. Mein Skript kann so viele Ebenen durchlaufen, wie ich für sinnvoll halte, in diesem Fall fünf. Dann habe ich den Speicherort für die heruntergeladenen Dateien und die Benennungsmethode festgelegt. Nachdem ich die Funktionsweise und die Feinheiten des Zugriffstokens (aus Sicherheitsgründen) erläutert habe, zeige ich Ihnen in diesem Beispiel, wie ich einige JMP-Dateien aus Box herunterlade.
Hier gibt es einige Funktionen zur rekursiven Suche in JSL-Dateien. Ich werde diese und die Python-Downloads nicht im Detail behandeln, sondern mich auf die Hauptverbindung zu Box konzentrieren. In diesem Fall ging es um Sicherheitsaspekte, die für jedes Unternehmen und jede Person relevant sein können. Der optimale Speicherort variiert je nach Kontext und Sicherheitslage. In meinem Fall habe ich die Verbindung hier belassen. Zunächst muss ich die Informationen von Box abrufen. Dazu benötige ich mein Zugriffstoken. Dieses erhalte ich über die verschlüsselte Datei secrets.jsl, die Sie gesehen haben. Sie liefert mir das Zugriffstoken, eine Variable in dieser JSL-Datei.
Da ich in diesem JSL-Code hier ein Zugriffstoken erhalte, könnte man es beispielsweise in Zeile 583 durch eine print-Anweisung anzeigen lassen. Das Schöne an Box ist, dass diese Zugriffstoken ablaufen. Es besteht also praktisch kein Risiko, dass sie später missbraucht werden. Das Risiko wird zwar geringer, bleibt aber dennoch bestehen, wenn man dieses Zugriffstoken ausgibt oder das Skript ausführt und es an dieser Stelle ausgibt.
Idealerweise – oder zumindest potenziell – könntest du, anstatt wie hier den Box-Zugriffstoken einzufügen, die Authentifizierungskomponenten (Tests und Suche) in meine `secret.jsl` verschieben und dort verschlüsseln. In diesem Fall wäre der Zugriff nicht mehr möglich und niemand könnte ihn einsehen. Das ist ein etwas anderer Anwendungsfall, den du nach Belieben verwenden kannst. Am sichersten wäre es, den Box-Zugriffstoken in der `secrets.jsl` zu speichern und zu verschlüsseln, sodass ihn niemand sehen kann. Aber in diesem Fall – und das ist ein guter Diskussionspunkt – habe ich es so eingerichtet, und so sieht es aus. Wir können dann weiter darüber sprechen.
Dann wird das Box-Zugriffstoken abgerufen, und anschließend kann ich die Box-Zugriffsdateien durchsuchen, da ich nun meine Authentifizierung habe, die meine Identität bestätigt und mir Zugriff gewährt. In meinem Fall verwende ich eine Demo, in der ich beispielsweise Daten aus einer Datei auf meinem Box-Laufwerk abrufe. Dieser Dateibereich sieht folgendermaßen aus: Er heißt „Secrets_JMP Discovery“ und enthält die Dateibeispiele „Secrets“. Ich werde einige JMP-Tabellen aus diesem Verzeichnis abrufen und zusammenführen. Dazu führe ich folgenden Befehl aus. Anschließend folgt die übliche Wartezeit, die beim Ausführen eines Skripts manchmal auftritt. Es kann einen Moment dauern.
Authentifizierung, sag mir, wer ich bin, sag mir, wer du bist. Da sind wir. Das ist die Datei, die ich bekommen habe. Du siehst meine Quelltabellen, das waren die Namen der JMP-Tabellen, die ich an diesem Box-Speicherort hatte, und das sind die Tabellen, die zusammengefügt wurden. Nur ein paar Scherze.
Genau das habe ich mit meinen Box-Zugriffstoken und der Integration mit JWTs gemacht. Ich werde es an Nathan zurückgeben.
Nach all dem, was Sage und ich gezeigt haben, stellt sich die Frage: Wie lässt sich das Ganze konkret umsetzen? Und wie können wir erreichen, dass nicht nur Sage und ich, sondern auch andere diese Arbeit übernehmen können? Wir haben das Ganze auf GitHub veröffentlicht. Hier ist der Link zu unserem GitHub-Repository. Es ist mit der Präsentation verlinkt und enthält unsere Beispiele.
Hier gibt es, je nachdem, wie Sie es organisieren möchten, viele Möglichkeiten. Das Wichtigste ist die Vorlage für die Geheimnisse. Diese hatte ich Ihnen bereits mit dem Basiswert gezeigt. Hier finden Sie verschiedene Beispiele. Diese Beispiele enthalten drei Hauptdateien. Hier ist unsere Vorlage mit allen benötigten Informationen. Sie können diese mit Ihren persönlichen oder Firmeninformationen ausfüllen. Hier wird die Verschlüsselung durchgeführt. Um die Sicherheit zu gewährleisten, empfehlen wir Ihnen, die Vorlage in Ihrem Repository oder Klon zu speichern und „template“ durch „raw“ zu ersetzen. So bleiben alle wichtigen Informationen verborgen. In der .gitignore-Datei ignorieren wir die Datei raw.jsl. Dadurch bleiben diese Dateien lokal und stehen Ihnen zur Bearbeitung zur Verfügung. Ihre Zugangsdaten bleiben sicher. Dann verschlüsseln Sie Ihre Originaldatei und laden sie als verschlüsselte Version hoch.
Auf GitHub sollte sich nun ausschließlich Ihre verschlüsselte und funktionsfähige Version befinden. In jedem dieser Ordner finden Sie spezifische Dokumentationen zu deren Verwendung. Wie viele Informationen Sie in Ihrer Haupt-README-Datei oder in Ihren Dokumentationen preisgeben möchten, hängt davon ab, welche Informationen Sie anderen Nutzern zur Verwendung dieser Daten vermitteln möchten. Wenn Sie beispielsweise Project Jab verwenden, das Sage und ich letztes Jahr vorgestellt haben, können Sie damit Add-ons erstellen und Dateien aus einzelnen Repositories einbinden. So können Sie gezielt die benötigten geheimen Dateien einbinden, um Ihre Anforderungen zu erfüllen. Dies ist eine elegante Möglichkeit, die Informationen verfügbar zu halten und gleichzeitig verschiedene Stufen der Vertraulichkeit zu gewährleisten.
Wie Sage bereits andeutete, kann es passieren, dass man, wenn der Code etwas komplexer wird, in eine Situation gerät, in der man, um dieses Maß an Geheimhaltung aufrechtzuerhalten, immer mehr Informationen in diese verschlüsselte Datei einfügt.
Wie Sage in ihrer Vorlage erwähnte, enthält sie viele Funktionen, die umfangreiche Aufgaben erledigen. In ihrem Beispiel wies sie jedoch auf dieses spezielle Zugriffstoken hin. Ich erinnere mich nicht mehr genau, wo es sich in der Funktion befindet, aber wenn Sie es nicht in Ihrem Hauptbeispiel anzeigen möchten, fügen Sie diese Funktion einfach Ihrer Vorlage oder Ihrer Vorlagendatei hinzu, verschlüsseln Sie diese, und schon ist alles verborgen. Die erforderlichen Schritte hängen von Ihren Anwendungsfällen und Ihren Zielen ab. In Snowflake erwies sich dies als relativ einfach, da ich die Verbindung direkt nutzen oder einfach eine Tabelle bereinigen konnte. Die Vorgehensweise kann je nach Ihren Anforderungen und den benötigten Verbindungen – ob APIs oder Ähnliches – variieren.
Ich denke aber, dass dies eine relativ leicht verständliche und anwendbare Methode ist, um Ihre Geheimnisse zu erläutern und viele Ebenen anzubieten, damit diejenigen ihre Programmierkenntnisse verbessern können, während gleichzeitig solide Cybersicherheitspraktiken beibehalten werden, um alles sauber und ordnungsgemäß zu halten.
Ich denke, damit hätten wir alles besprochen, was wir heute besprechen wollten. Vielen Dank an alle bei JMP für diese Gelegenheit und an alle Zuschauer fürs Einschalten.
Absolut einverstanden. Ich hoffe, deine Geheimnisse sind sicher. Wenn du magst, kannst du uns dieses Jahr auf der JMP Discovery besuchen, wir werden dort sein.
Ja, das werden wir. Danke.