Lecture seule, jamais cloné,
et aucun token dans la base de données.
- Accès au repository
- Lecture seule
- Repositories clonés
- Aucun, jamais
- Tokens en base de données
- Aucun venant de GitHub
- Markdown assaini
- Avant stockage
- Secret de clé stocké
- SHA-256 uniquement
Trois sortes, et ce qui advient de chacune
La règle qui les sous-tend toutes : nous conservons la forme la moins durable d'un identifiant qui fonctionne encore, et lorsque nous devons en conserver un, nous disons où et sous quelle clé.
GitHub
Une GitHub App, en lecture seule, sur les repositories que vous choisissez.
- Révocable depuis GitHub à tout moment, sans nous demander.
- Les tokens d’installation sont émis à la demande à partir de la clé privée de l’App et gardés en mémoire pendant leur heure. Rien qui ressemble à un token n’est jamais écrit en base.
- La clé privée est le seul secret de longue durée et sa place est dans un coffre à secrets, pas à côté des données.
- Connecter un repository interroge GitHub en tant que vous, pas en tant que l’installation — personne ne peut donc connecter un repository qu’il ne peut pas lire lui-même.
GitLab
Un token d'accès de projet ou de groupe que vous collez, chiffré au repos.
- Chiffré sous sa propre clé, délibérément pas le secret de session : changer le secret de session déconnecte tout le monde et relève de la routine, changer celle-ci implique de rechiffrer chaque identifiant.
- Un token portant un droit d’écriture est refusé à la connexion, en le nommant.
- Un token sans expiration est refusé d’emblée.
- La conséquence est assumée plutôt que contournée : comme nous ne détenons pas de token capable d’écrire, nous ne pouvons pas créer de webhooks GitLab, et la fraîcheur repose à la place sur une interrogation périodique.
Clés d'API
Pour les agents et la CI. Celles de l'organisation, pas les vôtres.
- Seul un SHA-256 de la clé est stocké. La valeur est affichée une fois, à l’émission, et il n’existe aucun moyen de la récupérer — car un tel moyen voudrait dire qu’elle est conservée quelque part.
- Une clé appartient à l’organisation plutôt qu’à son auteur, donc un job de CI ne casse pas le jour où cette personne change d’équipe.
- Une clé désigne exactement un projet. Il n’est plus possible d’émettre une clé valable pour toute l’organisation.
- La révocation prend effet à la requête suivante. Rien ne met la vérification en cache.
Le Markdown autorise du HTML brut, et c'est le risque le plus grave dans un produit comme celui-ci
Une balise <script> dans le README de quelqu’un s’exécuterait sinon sur notre origine, avec la session du lecteur. Chaque document est assaini côté serveur selon une liste autorisée avant d’être stocké en HTML — pas filtré au moment du rendu, où un seul chemin qui oublie est un chemin qui livre une faille.
Le Markdown brut reste la source de vérité. Le HTML rendu n’est qu’un cache, indexé par le hash du contenu et une version du moteur de rendu : durcir le moteur invalide tout d’un coup et rien n’a besoin d’être migré.
Le repository n'est jamais cloné, et rien hors de votre documentation n'est jamais récupéré
Un seul appel d’API renvoie la structure des dossiers plus un hash de contenu par fichier. Pas de binaire git, pas de copie de travail, rien d’écrit sur disque. Seuls les blobs Markdown sont récupérés, et uniquement dans la racine de la documentation — un fichier source est un nom que nous pouvons voir dans un chemin, jamais un contenu que nous récupérons.
Les images d’un repository privé passent par un proxy authentifié au lieu d’être liées directement, donc elles se chargent pour les personnes autorisées à les voir et pour personne d’autre.
Les cookies de session sont chiffrés, pas seulement signés
Une session OAuth porte le token GitHub de l’utilisateur, donc un cookie simplement signé serait lisible. Les cookies sont chiffrés ; les mots de passe sont hachés avec Argon2 derrière un port, jamais avec une bibliothèque dont Python a retiré la dépendance.
La révocation est réelle et non nominale. Chaque requête authentifiée compare une époque sur la ligne du compte, ce qui est le prix honnête du retrait d’un identifiant porteur de sa propre validité — un cookie déchiffré n’est pas un cookie authentifié. Déconnecter tous les appareils est une seule action, et déconnecter GitHub invalide les sessions qui portaient son token au lieu de les laisser fonctionner jusqu’à expiration.
Il existe une liste des sessions : une étiquette de plateforme approximative et une adresse tronquée à un /24. Pas de ville, pas de pays, et aucune supposition sur le caractère inhabituel d’une connexion — il faudrait une base de géolocalisation ou un modèle de l’habituel, et inventer l’un ou l’autre, c’est ainsi qu’une liste d’appareils se met à signaler des incidents qui n’ont jamais eu lieu.
Rien n'est lisible par défaut, et le navigateur ne décide jamais
Rejoindre une organisation ne donne accès à aucun document. Un membre lit un projet grâce à un accès d’équipe ou à un accès direct ; quelqu’un sans équipe ni accès ne lit rien du tout. Aucun réglage de visibilité ne donne discrètement accès à tout le monde.
L’accès se résout côté serveur, dans la seule requête par laquelle passe déjà chaque route de lecture. Un repository auquel vous avez perdu l’accès répond 404 — exactement comme un repository qui n’a jamais existé, donc la réponse ne permet pas d’apprendre ce qui existe.
Aucune comparaison de rôles n’a lieu en TypeScript. Même les réponses ligne par ligne « cette personne peut-elle être retirée » affichées sur l’écran des membres sont calculées sur le serveur, donc aucune copie de la hiérarchie n’atteint le client pour y diverger.
Une URL que vous fournissez est traitée comme une surface d'attaque
Un hôte GitLab autogéré est une URL qu’un client saisit et que le serveur va ensuite chercher, ce qui est une primitive SSRF. Les vérifications sont des prédicats purs : https uniquement, chaque adresse résolue confrontée aux plages privées, de bouclage, link-local et réservées, et les redirections refusées d’emblée plutôt que suivies puis revérifiées.
L’ordre d’un webhook est fixe et jamais réarrangé : corps brut, puis signature comparée en temps constant, puis analyse. Lire un payload avant de le vérifier, c’est analyser ce qu’un inconnu a envoyé.
L’adresse du client relève de la configuration, pas d’un en-tête. Par défaut, les en-têtes transmis sont entièrement ignorés ; quand les proxys sont comptés, l’adresse est prise du côté qu’un appelant ne peut pas étendre en ajoutant des valeurs en tête.
Un identifiant machine est plus restreint que la personne qui l'a créé, à dessein
Une clé peut porter un état de relecture minimal, et les documents en dessous répondent 404 à chaque porte — routes de lecture comprises, car un seuil qui s’arrête à une surface est un seuil avec une seconde porte d’entrée. Il vit sur l’identifiant plutôt que dans un argument d’outil : un filtre que l’agent transmet est un filtre qu’on peut l’inciter, par prompt, à ne plus transmettre.
Aucun outil de cette surface ne modifie un document du repository, ne ferme la note d’un relecteur, ne supprime un artifact, ni ne pose une question libre à un modèle. Ce sont des absences, pas des réglages — aucun drapeau qu’un administrateur pourrait accorder, et c’est tout l’intérêt.
Le texte des documents ne quitte la machine que pour une organisation qui l'a demandé
C’est le seul chemin sortant qui transporte du contenu client, et l’interrupteur est désactivé par défaut — l’activer par défaut aurait fait envoyer des documents à un tiers par chaque organisation existante, simplement parce qu’un déploiement a eu lieu. Son activation est inscrite au journal d’audit.
Un document vide ne déclenche aucun appel : demander à un modèle de ne rien inventer est une requête, alors que ne pas l’appeler est une garantie. Un fournisseur nommé sans sa clé refuse au lieu de renvoyer un texte factice, car un résumé inventé stocké contre un vrai contenu est servi comme vrai jusqu’à ce que ce contenu change.
Une ligne d'audit est écrite dans la même transaction que le changement
Jamais après le commit, jamais au mieux, jamais depuis une tâche de fond. Si le changement est annulé, l’enregistrement part avec lui, et si l’enregistrement ne peut pas être écrit, le changement n’a pas lieu. Le nom de l’auteur et le libellé du sujet sont copiés sur la ligne, car une équipe supprimée doit encore se lire comme l’équipe qu’elle était.
Le journal est filtrable et exportable, et conservé pendant la durée fixée par votre plan. Rien ne peut en supprimer une seule ligne — un appelant capable de retirer une ligne est capable de retirer la plus intéressante.
Ce que vous pilotez vous-même
Deux d'entre eux dépendent du plan et la page dit lesquels, car l'apprendre au moment d'en avoir besoin est trop tard.
Liste d'adresses autorisées
Refusez toute requête venant d’une adresse que vous n’avez pas nommée. Cela s’applique aux clés d’API comme aux navigateurs — un identifiant machine est vérifié à la porte avant que la requête soit traitée.
Connexion GitHub obligatoire
Retirez les documents à quiconque n’a pas d’identité GitHub, pour que l’accès suive un compte que votre organisation peut voir.
Politique de session
Définissez la durée de vie d’une session, et déconnectez tous les membres d’un coup quand c’est nécessaire.
Rétention de l’audit
Choisissez combien de temps le journal est conservé. Une période plus longue est l’avantage ; la purge suit un calendrier et ne se lance pas à la demande.
Ce que nous n'avons pas fait
Une page sécurité sans aucune lacune est une page sécurité que personne ne croit. Celles-ci sont réelles.
- SSO et SAML ne sont pas réalisés
- La carte existe dans les réglages de sécurité de l’organisation et c’est du balisage statique. Elle fait partie de ce que couvre un contrat Enterprise et ne peut pas être activée aujourd’hui. Nous préférons le dire ici plutôt que de vous laisser le découvrir après signature.
- Pas de SOC 2, pas d’ISO 27001, pas de test d’intrusion
- Rien de tout cela n’a été fait. Si votre processus d’achat en exige un, nous ne sommes pas encore prêts pour vous, et c’est une vraie réponse plutôt qu’une promesse de feuille de route.
- Le DNS rebinding est bloqué ; l’épinglage de connexion ne l’est pas
- Les vérifications d’adresse ont lieu à la résolution. Un hôte qui répond différemment à la seconde résolution est géré, mais nous n’épinglons pas la connexion à l’adresse validée.
- Pas de clés gérées par le client sur un déploiement que nous exploitons
- Les clés de chiffrement appartiennent au déploiement. Détenir les vôtres, c’est exploiter le vôtre : une installation dans votre réseau relève d’un contrat Enterprise, et sur le déploiement que nous exploitons, les clés restent les nôtres.
Vous avez trouvé une erreur dans tout cela ? [email protected]. Nous préférons l’apprendre de vous que le lire ailleurs.
Donnez-lui un accès en lecture à un repository et retirez-le en un clic.
Le plus petit essai possible de tout cela, c’est un repository public et cinq minutes.
Commencer gratuitement