Bonjour à tous. Je m'appelle Nathan Clark. J'utilise JMP depuis 22 ans et je travaille chez IDEXX Laboratories. Je présenterai la gestion des secrets avec ma co-présentatrice, Sage Darling.
Salut. Je suis Sage. Je travaille chez IDEXX avec Nathan. Je suis ingénieure analytique senior et nous allons vous parler de secrets.
Excellent. Commençons par définir ce qu'est un secret. En général, du point de vue du code, un secret est une forme d'identifiant nécessaire au bon fonctionnement du code pour accéder aux ressources, mais que personne d'autre ne doit connaître. C'est comparable au mot de passe que vous utilisez pour vous connecter à votre ordinateur : vous devez l'utiliser régulièrement, mais vous ne devez le communiquer à personne.
Les secrets sont des mécanismes développés par différentes plateformes pour garantir la confidentialité des identifiants une fois divulgués. Ils permettent ainsi une utilisation beaucoup plus sûre des tâches en arrière-plan. JMP n'intègre pas cette fonctionnalité nativement. Cependant, avec l'essor du code et des opérations en arrière-plan (ou « en coulisses ») dans JSL, nous avons souhaité disposer de cette possibilité afin de pouvoir partager nos activités et accéder à différentes ressources sans divulguer accidentellement des informations confidentielles.
Lors d'une discussion que j'ai eue il y a quelque temps avec le support JMP, j'ai obtenu ce petit bout de code écrit par, je crois, Evan McCorkle, qui l'a conçu comme un moyen de masquer ces secrets et de permettre leur existence, comme nous le voyons ici, mais ensuite vous pouvez chiffrer le script et l'utiliser.
En gros, le système utilise une fonction contenant les secrets, mais requiert un secret secondaire, ou une phrase d'accès, dont l'existence est connue et que toutes les fonctions du code chiffré possèdent. Personne d'autre ne peut la connaître. Même si quelqu'un sait que le secret A correspond à la fonction qu'il souhaite appeler, il ne peut accéder directement à ces secrets sans connaître cette phrase. Une fois chiffrée, cette phrase peut être intégrée comme script secret, permettant ainsi d'accéder à ces informations à l'insu de l'utilisateur. Lors de mes échanges avec le support JMP, malgré leurs efforts, ils n'ont pas trouvé de moyen de révéler le secret lors des tests. Je n'y suis pas parvenu non plus avec mes propres développements. Ce système semble être un excellent point de départ pour stocker des secrets de manière sécurisée tout en permettant leur utilisation pour diverses opérations de programmation.
Nous avons deux exemples aujourd'hui. Je vais vous parler d'un exemple de mise en place pour Snowflake. Voici le modèle de connexion Snowflake. Il s'agit de la fonction que j'ai écrite, dont les détails techniques sont masqués.
Je vais agrandir un peu ce tableau. Vous voyez, j'ai la fonction `get_secrets` qui indique où se trouvent les secrets. Dans mon cas, pour ce que nous voulons faire, j'ai seulement besoin d'un nom d'utilisateur et d'un mot de passe pour accéder à Snowflake avec la configuration actuelle. Il s'agit d'un tableau de mots de passe relativement simple à gérer.
Mais j'ai aussi d'autres fonctions de support pour les utiliser. La première établit une connexion à la base de données et renvoie simplement ce nouveau connecteur de données. Il s'agit d'une chaîne de caractères que vous devez composer. Ce connecteur de données permet de se connecter à tous les éléments spécifiques à votre entreprise. La seule chose requise est l'installation du pilote Snowflake. Il s'agit du nom du pilote Snowflake lorsque vous l'installez depuis le site web de Snowflake sur votre ordinateur. Vous pouvez ainsi établir la connexion ODBC. Votre base de données sera spécifique à votre entreprise. Cependant, les serveurs de votre entreprise sont connectés à Snowflake, etc. Ensuite, le mot de passe est récupéré dans un autre tableau que nous avons obtenu. Il est inséré, puis vous avez ici vos autres éléments auxquels vous pouvez vous connecter.
La dernière fonction va encore plus loin. Elle prend en entrée la chaîne de caractères de la requête à exécuter, effectue toutes les connexions nécessaires et exécute la requête. Enfin, elle supprime tous les scripts de table associés. J'expliquerai son utilité dans l'exemple. En résumé, il s'agit d'une couche de protection supplémentaire qui empêche toute divulgation accidentelle des identifiants.
Voici un exemple très simple. Dans le premier exemple, il s'agit d'une simple requête SQL brute, et j'utilise du SQL personnalisé en insérant simplement la version chaîne de caractères de la requête. Si j'exécute ce code, j'obtiens une table depuis Snowflake. Elle ressemble beaucoup à BigClass, car c'est le cas : j'en ai simplement importé une copie sur Snowflake. Vous obtenez ainsi la table exactement comme vous le souhaitez. Elle inclut certaines informations sources, ce qui peut s'avérer utile. J'ai remarqué que cette méthode masque le nom d'utilisateur et le mot de passe, comme prévu. En explorant différentes approches, j'ai constaté, à plusieurs reprises (par hasard ou non), que le mot de passe n'était pas exposé, mais que l'identifiant utilisateur l'était. Selon le niveau de sécurité requis pour la surveillance de ces informations d'identification, j'ai créé une fonction supplémentaire qui supprime ces données.
Si je commente cette section et que j'exécute le code, cela crée une nouvelle table avec l'autre fonction, en utilisant cette requête. Le résultat est identique, sauf que cette fois-ci, la table ne contient aucun script. C'est pratique. Pour illustrer davantage le fonctionnement, prenons l'exemple suivant : si je veux supprimer ces données et sélectionner uniquement ce graphique en étoile, ainsi que toutes les colonnes, cela devrait fonctionner et les importer. Il en va de même si vous souhaitez ajouter des options de filtrage. C'est possible ici. Le fonctionnement est identique à celui du langage SQL standard pour ce type de requêtes. Il suffit de passer la chaîne de caractères en entrée pour obtenir la table de données nettoyée. C'est un exemple relativement simple. Je laisse maintenant la parole à Sage, qui propose une version plus complexe de l'intégration de cette fonctionnalité avec la superposition de fonctions. Je passe la main à Sage.
FoxDrive est un service de stockage de dossiers en ligne. Nous l'utilisons pour de nombreuses choses, et c'est pourquoi j'ai voulu commencer par là : comment m'authentifier auprès de Box pour récupérer des fichiers localement sur mon ordinateur ? Cela nécessite la création d'une application via Box Developers, qui me permettra ensuite de m'authentifier. Cette application requiert un jeton JWT (JSON Web Token), lequel est ensuite utilisé pour obtenir un jeton d'accès me permettant d'accéder à Box. C'est là que ma situation s'est complexifiée, car je n'avais jamais manipulé de JSON ni de JWT auparavant. C'est donc ce que j'ai dû implémenter.
Comme Nathan l'expliquait, voici à droite un exemple de mon fichier secrets.jsl. Il n'est pas chiffré pour le moment, vous pouvez donc voir son contenu. Mon fichier commence par une fonction `get secrets`. C'est ici que sont stockées toutes les informations JWT. Le JWT contient des informations telles que l'identifiant client, le secret client et des éléments uniques à cette application. Je les place dans un tableau associatif afin de constituer les secrets qui seront ensuite extraits. Pour accéder à chacun de ces éléments, j'ai d'autres fonctions qui parcourent les tableaux associatifs, en commençant par l'identifiant client.
Je vais n'en montrer qu'un seul, car si je les passais tous en revue, vous verriez tous les éléments du tableau associatif, ce qui serait probablement fastidieux. Voici un exemple permettant d'obtenir uniquement l'identifiant client. À l'intérieur, j'utilise les secrets pour obtenir un autre secret, à savoir ma clé d'accès Box, qui me donne ensuite l'identifiant client. Encore une fois, tout reste secret afin de pouvoir extraire des données de cette unique fonction grâce au chiffrement.
Ce que j'ai dû ajouter, et qui diffère légèrement du reste, concerne l'obtention du jeton JWT Box. Cela requiert une approche un peu plus subtile, car les JWT nécessitent des assertions pour obtenir un nouveau jeton d'accès, lequel requiert une signature. Cette signature étant une information sensible, elle est considérée comme telle. Mon fichier secrets.jsl contient des fonctions qui effectuent un traitement supplémentaire pour obtenir ces informations de Box. Dans ce cas précis, ma fonction JWT… Je vais l'encadrer pour plus de clarté. Ma fonction JWT crée cette assertion auprès de Box afin de pouvoir effectuer l'envoi de la requête pour obtenir le jeton d'accès. Pour ce faire, je dois obtenir les informations explicites mentionnées précédemment dans mes secrets : mon ID d'entreprise GetBox, mon ID client GetBox, ma clé publique Box, ma clé privée et une paraphrase unique à mon application.
À partir de là, j'ai des outils de dépannage pour gérer les clés manquantes et identifier les éléments essentiels. Pour effectuer l'assertion JWT, j'ai dû utiliser Python. Cela inclut également un autre exemple d'utilisation d'un langage externe dans le fichier secrets.jsl, ce qui a nécessité quelques ajustements pour garantir l'affichage des informations. C'est pourquoi ces données sont chiffrées dans secrets.jsl et non directement dans le script qui les utilise pour accéder aux informations JWT. C'est aussi une occasion unique de voir Python intégré à ce JSL, ce qui est vraiment intéressant.
J'envoie ensuite ces informations au script Python, qui récupère une signature spécifique pour ce JWT. À partir de là, il crée les identifiants et renvoie l'assertion JWT afin que je puisse l'utiliser ultérieurement. Grâce à cette assertion JWT, j'obtiens mon jeton d'accès. J'obtiens alors mon jeton JWT Box (comme vous l'avez vu précédemment), puis j'utilise mon ID client et mon secret client pour créer un jeton en combinant l'assertion JWT, mon ID client et mon secret client, puis en envoyant une requête HTTP à Box. J'obtiens ainsi mon nouveau jeton d'accès.
J'ai également prévu un système de récupération d'informations pour les personnes intéressées par une analyse plus approfondie, qui enverra ensuite des informations concernant l'obtention ou non du jeton d'accès, puis renverra ce jeton à l'utilisateur.
Où cela s'utilise-t-il ? Comment cela s'utilise-t-il ? Je vais vous montrer un exemple, car nous pourrions approfondir la question des jetons d'accès. Pour l'intégration, je vous présenterai un dossier générique à titre d'exemple, mais dans mon code, je me contenterai d'afficher quelques entrées.
J'inclus ici la connexion Box, chiffrée dans mon cas, car c'est ce que vous devez faire une fois le fichier de secrets traité. Box utilise un système d'identifiants pour ses dossiers, permettant de savoir précisément dans quel dossier effectuer la recherche de fichiers. C'est ici que vous devez indiquer votre identifiant de dossier unique. Dans cet exemple, je récupère uniquement des fichiers JMP. La procédure pourrait être bien plus complexe selon vos besoins, mais mon exemple ne concerne que les fichiers JMP.
J'ai également inclus un paramètre permettant de contrôler la profondeur de récursion. Box est intéressant car son fonctionnement est orienté objet. On dispose d'un identifiant de dossier, puis d'identifiants à l'intérieur de ce dossier ; la recherche doit donc être récursive. Mon script peut parcourir autant de niveaux que nécessaire, ici cinq. Je définis ensuite un emplacement de téléchargement pour mes fichiers, ainsi qu'une méthode pour les nommer. Après avoir expliqué le fonctionnement et les subtilités liées au jeton d'accès (pour des raisons de sécurité), je vais vous montrer un exemple concret : je vais extraire des fichiers JMP depuis Box.
Ce code contient des fonctions permettant de rechercher récursivement dans les fichiers JSL. Je ne vais pas les détailler, ni le téléchargement de Python, mais je vais me concentrer sur la connexion principale à Box. Dans ce cas précis, c'est ici que nous avons abordé les questions de sécurité, notamment concernant l'emplacement optimal de cette connexion. Cet emplacement varie légèrement d'une entreprise ou d'une personne à l'autre, tout comme les exigences de sécurité. Personnellement, je l'ai laissée ici. Pour commencer, cette fonction principale nécessite la récupération des informations depuis Box. Pour cela, je dois obtenir mon jeton d'accès. L'obtention de ce jeton fait partie du fichier secrets.jsl chiffré que vous avez vu. Ce jeton d'accès est stocké dans une variable du fichier JSL.
Dans ce JSL, comme je récupère ce jeton d'accès, si j'ajoutais une instruction d'affichage à la ligne 583, par exemple, vous pourriez voir ce jeton. L'avantage de Box, c'est que ces jetons expirent. Il n'y a donc pratiquement aucun risque qu'ils soient utilisés ultérieurement. Le risque est minime, mais il existe toujours un risque potentiel de sécurité si vous affichiez ce jeton d'accès ou si vous obteniez le script, l'exécutiez et affichiez le jeton à cet emplacement.
Idéalement, au lieu de placer le jeton d'accès Box ici, je pourrais déplacer les éléments d'authentification (tests et recherches) dans mon fichier secret.jsl, puis les chiffrer. Ainsi, il ne serait plus accessible et personne d'autre ne pourrait le voir. C'est un cas d'utilisation légèrement différent, et vous pouvez l'utiliser à votre guise. La méthode la plus sûre consiste à placer le jeton d'accès Box dans le fichier secrets.jsl et à le chiffrer pour qu'il reste invisible. Mais dans ce cas précis, c'est un bon point de départ : voici comment je l'ai laissé, voici à quoi cela ressemble. Nous pouvons en discuter à partir de là.
Ensuite, le système récupère le jeton d'accès Box, ce qui me permet de rechercher des fichiers d'accès Box. Mon authentification confirme mon identité et autorise l'accès. Dans mon cas, j'ai une démonstration avec un exemple d'extraction de données depuis un fichier sur mon disque Box. Ce fichier se présente sous le nom « Secrets_JMP Discovery », suivi de « File Examples_Secrets ». Je vais extraire et concaténer des tables JMP à partir de cet emplacement. Pour ce faire, j'exécute le script suivant. Ensuite, une brève musique d'attente se fait entendre, ce qui est normal lors de l'exécution d'un script. Cela peut prendre quelques secondes.
Authentification, dites-moi qui je suis, dites-moi qui vous êtes. Voilà. Voici le fichier que j'ai reçu. Vous pouvez voir mes tables sources ; ce sont les noms des tables JMP que j'avais à cet emplacement Box, et voici les tables qui ont été concaténées. Juste quelques blagues.
Voilà ce que j'ai fait avec mes jetons d'accès Box et l'intégration avec les JWT. Je vais transmettre le tout à Nathan.
Compte tenu de tout ce que Sage et moi avons présenté, comment cela va-t-il être concrètement mis en œuvre et comment faire en sorte que, maintenant que c'est fait, d'autres personnes que Sage et moi puissions y contribuer ? Nous avons donc publié le projet sur GitHub. Voici un aperçu : le dépôt est disponible. Il est également lié à la présentation et contient nos exemples.
Voilà, selon la façon dont vous souhaitez l'organiser, vous avez de nombreuses options. Mais l'élément principal que nous avons créé est le modèle de secrets. C'est celui que je vous ai montré précédemment avec sa valeur de base. Ensuite, nous avons différents exemples. Dans ces exemples, nous avons trois fichiers principaux : notre modèle, qui contient toutes les informations nécessaires, et celui-ci, que vous pouvez compléter avec vos informations personnelles ou celles de votre entreprise. Ce dernier fichier définit également le chiffrement. Pour une sécurité optimale, nous vous recommandons d'enregistrer ce modèle dans votre dépôt ou votre clone, en remplaçant « template » par « raw ». Ainsi, toutes les informations importantes sont masquées. Dans le fichier .gitignore, nous ignorons le fichier raw.jsl. Vos identifiants restent ainsi en local. Vous conservez vos identifiants. Ensuite, vous chiffrez votre fichier brut, puis vous le téléchargez en tant que version chiffrée de ce fichier.
Désormais, seule votre version chiffrée et fonctionnelle devrait se trouver sur GitHub. Chaque dossier contient une documentation spécifique à son utilisation. Le niveau de détail à partager, que ce soit dans votre fichier README principal ou dans votre documentation, dépend de ce que vous souhaitez que les utilisateurs sachent sur l'utilisation de ces informations. Si vous utilisez un outil comme Project Jab, présenté par Sage et moi l'année dernière, vous pouvez créer des extensions et potentiellement récupérer des fichiers depuis différents dépôts. Ainsi, vous pouvez récupérer uniquement les fichiers secrets nécessaires à votre application. C'est une excellente façon de rendre ces informations accessibles tout en maintenant différents niveaux de confidentialité.
Comme Sage l'a laissé entendre, si votre code devient un peu plus complexe, vous pourriez vous retrouver dans une situation où, pour maintenir ce niveau de confidentialité, vous finissez par inclure de plus en plus d'informations dans ce fichier chiffré.
Comme Sage l'expliquait dans son modèle, elle a inclus toutes ces fonctions qui effectuent un travail considérable. Mais c'est dans son exemple qu'elle a mis en évidence ce jeton d'accès spécifique. Je ne me souviens plus exactement où il se trouve dans la fonction, mais si vous ne souhaitez pas qu'il soit visible dans votre exemple principal, il vous suffit d'ajouter cette fonction à votre modèle ou à votre fichier brut généré par modèle, puis de le chiffrer. Ainsi, tout est masqué. Les étapes à suivre dépendent de vos cas d'utilisation et de vos objectifs. Dans Snowflake, cela s'est avéré relativement simple car j'ai pu utiliser la connexion directement ou simplement nettoyer une table. Votre expérience peut donc varier en fonction de vos besoins de connexion, qu'il s'agisse d'API ou autres.
Mais je pense que c'est une manière relativement simple à comprendre et à utiliser pour passer en revue vos secrets et être en mesure d'offrir de nombreux niveaux permettant à ceux qui le souhaitent d'améliorer leurs connaissances en programmation, tout en maintenant des pratiques de cybersécurité solides pour garantir la propreté et la conformité des systèmes.
Je crois que cela conclut notre discussion d'aujourd'hui. Un grand merci à toute l'équipe de JMP pour cette opportunité et à tous les spectateurs pour leur visionnage.
Tout à fait d'accord. J'espère que vous gardez bien vos secrets. Si vous le souhaitez, vous pouvez venir nous rencontrer au salon JMP Discovery cette année, car nous y serons.
Oui, nous le ferons. Merci.