Aller au contenu principal

Sécurité

🔒 Tous les accès aux outils doivent être sécurisés par authentificationà deux facteurs (2FA).

🌐 Les variables d'environnement doivent être utilisées pour tout ce qui est secrets, tokens, logins, urls, hostnames, etc.

🛡️ La gestion des secrets est assurée par des sealed-secrets qui versionnent les secrets chiffrés dans GIT.

Outils

DashLord : dashboard de sécurité et accessibilté

Accessible ici (se connecter pour voir plus d'informations) : https://dashlord.fabrique.social.gouv.fr.

Voir aussi : https://doc.incubateur.net/communaute/travailler-a-beta-gouv/jutilise-les-outils-de-la-communaute/dashlord.

SonarCloud : analyseur statique de code

Les produits de l'organisation sont tous scannés et les résultats sont accessibles ici : https://sonarcloud.io/organizations/socialgouv.

Talisman : prévention de publication de secrets

Publier involontairement un secret (par exemple un jeton d'accès) sur un dépôt public peut avoir beaucoup de conséquences indésirables. Une manière efficace d'éviter cela est d'exécuter un détecteur de secrets comme talisman sur le hook pre-commit de git.

yarn add -D husky is-ci node-talisman

# installer husky seulement si hors environnement de CI
npm pkg set scripts.postinstall="is-ci || husky install"

# installation de husky grâce au script de postinstall
yarn

# exécuter node-talisman sur le hook de pre-commit
# on détecte ici si l'interaction via un terminal est possible afin de ne pas
# crash quand le pre-commit est lancé par une application comme VSCode
mkdir .husky
echo "if sh -c ': >/dev/tty' >/dev/null 2>/dev/null; then exec </dev/tty; yarn node-talisman --githook pre-commit -i; else yarn node-talisman --githook pre-commit; fi" > .husky/pre-commit

# informe Talisman que le repo est en JS/TS
# permet d'éviter de scanner yarn.lock par exemple
echo "scopeconfig:\n - scope: node" > .talismanrc

On utilise husky pour gérer simplement le hook. Si vous utilisez déjà un gestionnaire de hooks, vous pouvez y ajouter node-talisman de manière similaire.

On pourra observer des cas de faux positif de talisman, par exemple sur des migrations SQL ou des données en base64. Dans ce cas, on lit attentivement le rapport, et on ajuste le fichier .talismanrc en fonction.

ClamAV : scan antivirus de fichiers

attention

Ce service de la Fabrique est expérimental. Aucune application ne doit bloquer sur le scan antivirus car le service pourrait être indisponible.

Pour le moment, il est nécessaire de demander explicitement à l'équipe SRE la création de l'instance associée à votre startup.

Lorsqu'un produit propose à ses utilisateurs de téléverser des fichiers, il est recommandé de scanner les fichiers pour y détecter de potentiels virus. Dans ce but, la Fabrique met à disposition un service ClamAV.

Implémenter dans l'application la communication avec le service antivirus. On passera par une interface REST afin d'envoyer un ou plusieurs fichiers et la réponse mentionnera pour chacun d'eux si le fichier semble sain.

Le service REST utilisé est celui-ci : https://github.com/benzino77/clamav-rest-api.

Il est nécessaire d'envoyer les fichiers à https://antivirus.fabrique.social.gouv.fr/{startup}/api/v1/scan encodés avec multipart/form-data et sous la clé FILES.

info

Le service ClamAV n'est accessible que depuis l'intérieur de notre infrastructure. Un scan ne peut donc être demandé que depuis le backend des applications, le front-end n'y a pas accès. En outre, lors du développement local, le service n'est pas accessible.

Exemple NodeJS :

const fs = require("fs");

const formData = new FormData();
formData.append("FILES", new Blob([fs.readFileSync("file1.txt")]), "file1.txt");
formData.append("FILES", new Blob([fs.readFileSync("file2.jpg")]), "file2.jpg");

const res = await fetch(
"https://antivirus.fabrique.social.gouv.fr/vao/api/v1/scan",
{
method: "POST",
body: formData,
},
);

console.log(await res.json());

Best practices

Les cheat sheets OWASP sont une très bonne référence.

CODEOWNERS

Les workflows d'intégration et déploiements continus des repositories SocialGouv sont protégés par la convention CODEOWNERS : tout changement impactant potentiellement l'infrastructure doit être revue par une personne de l'équipe OPS ou SRE. Ils seront automatiquement assignés aux issues qui touchent aux fichiers de CI lors d'une pull-request.

Third-parties

De manière générale il est déconseillé de référencer des scripts externes dans ses applications, comme des scripts ou CSS via CDN, google fonts ou autres services tiers; Privilégier l'utilisation de librairies dédiées que vous pouvez embarquer dans l'application elle-même.

Maintenance des dépendances

Les packages utilisés dans les applications doivent être maintenus à jour et scannés régulièrement, idéalement dans la CI.

Les packages non utilisés ou obsolètes doivent être supprimés.

Utilisez renovate pour maintenir votre application à jour et prévoyez le temps nécessaire dans les sprints.

Contrôles d'accès

La mise en place d'un middleware de RBAC par lequel toutes les requêtes entrantes passent permet de rejeter au plus tôt les requêtes illégitimes et de mettre en place une liste blanche de pages ou endpoints non protégés (le comportement par défaut étant "protégé" pour éviter l'introduction de défauts de contrôle d'accès au fur et à mesure des développements).

Ex : https://github.com/nyambati/express-acl

Mettre en place une matrice des rôles qui associe à chaque type de donnée des permissions de type lecture/écriture par rôle.

Leak d'informations

Les informations techniques ne doivent pas être exposées au runtime. Les serveurs et applications ne doivent pas fournir de header ou signature permettant de les identifier. (ex: header Served-by)

Les données de développement (GIT et bases de données) doivent être considérées comme publiques et ne pas utiliser de données sensibles ou personnelles.

Les développeur(se)s ne doivent en aucun cas recevoir de données de production sur leur poste de travail. Les équipes de dev doivent mettre en oeuvre des mécanismes de seeds pour travailler avec des volumes de données réalistes.

Les applications ne doivent jamais logger d'information confidentielle ou de credentials sur la console. En effet ces informations pourraient remonter sur les outils de logging tels Sentry ou grafana.

Sécurité navigateurs

  • Définir une content security policy (CSP) stricte, comme par exemple : en-tête HTTP Content-Security-Policy: default-src 'self'; frame-ancestors 'self'; Utiliser un outil comme Laboratory pour profiler votre application et vérifier les headers CSP.
  • Définir l'attribut "integrity" sur l'ensemble des ressources link et script de la page (SubResource Integrity).
  • Gestion des cookies : utiliser les attributs de cookie HttpOnly, Secure et SameSite. Ne pas mettre SameSite à "None".
  • Auto-héberger l'ensemble des ressources de la page. Pour celles qui ne peuvent pas l'être et pour les traitements de moindre confiance, utiliser un WebWorker ou une iFrame avec l'attribut "sandbox".
  • Les verbes HTTP doivent être respectés, les opérations GET ne doivent pas modifier de données.
  • En cas d'utilisation de sessions, les opérations qui impactent des données doivent être protégées des attaques de type CSRF avec un système de jeton.

Ex: https://github.com/helmetjs/helmet

Sessions

  • Les sessions des utilisateurs authentifiés doivent expirer automatiquement et pouvoir être fermées par l'utilisateur (cette action doit effectivement supprimer la session côte serveur).
  • Les sessions doivent pouvoir être fermées par des administrateurs
  • Durée : Le délai doit être adapté à la durée d'utilisation légitime prévue (pour les utilisateurs authentifiés) et à la sensibilité des données. Ex: 6h pour une sécurité moyenne

Mots de passe

Robustesse

Les empreintes de mot de passe doivent être stockées de façon sécurisée, en s'en remettant à une implémentation proposée par le framework ou langage utilisé, après avoir vérifié qu'il implémente correctement une fonction adéquate pour le stockage des empreintes, telle que PBKDF2, Bcrypt, Scrypt ou Argon2, avec des contraintes temps/mémoire adaptées.

Forcer la complexité du mot de passe à : minimum 12 chars, 1 majuscule, 1 minuscule, 1 chiffre, 1 caractère spécial

Proposer à l'utilisateur de lui générer.

Procédure de changement de mot de passe

Avertir par email l'utilisateur en cas de changement de mot passe.

Procédure de reset de mot passe

Voir références OWASP et building a secure password reset feature

Avertir par email l'utilisateur en cas de changement de mot passe.

Voir aussi cet article sur les forms de logins et passwords managers

DDOS

Les mesures de prévention anti-DDOS et Waf doivent être mis en place en amont de l'application (côté infra/reverse-proxy)

Côté applicatif, l'utilisation de fonctions synchrones trop gourmandes en CPU (exemples : readFileSync, jwt.verify sans callback, bcrypt.hashSync, bcrypt.genSaltSync) présente deux types de risques :

  • côté serveur : vulnérabilité augmentée au déni de service (DOS), l'attaquant pouvant cibler les pages qui mettent en oeuvre ce type de fonctions côté serveur pour diminuer le coût de l'attaque.

  • côté client : le blocage du fil d'exécution principal se traduit en un "freeze" désagréable de la page

Il est recommandé de faire appel à la version asynchrone de ces fonctions, ou de les wrapper dans un thread ou web worker si elles n'ont pas d'implémentation asynchrone disponible.

Logging

Exceptions

Journaliser explicitement les erreurs issues de la logique de l'application, qui sont inconnues du runtime. par exemple à l'aide de sentry. La remontée d'exceptions dans sentry doit veiller à ne pas remonter de données sensibles ou personnelles (ex: cookies).

Logs applicatifs

  • L'application doit logger en JSON (cf 12 factors apps)
  • L'application ne doit logger QUE les données nécessaires au bon fonctionnement du service
  • Les utilisateurs de l'application doivent être informés de cette journalisation
  • Ces données techniques ne doivent PAS contenir de données permettant d'identifier un individu
  • Les logs peuvent être conservés jusqu'à 12 mois si nécessaire
Position de la CNIL

“De ce point de vue, l’enregistrement de ces données de journalisation ne rend pas le traitement plus intrusif, sous réserve que leur existence ne mène pas à un dépassement de la durée de conservation des données. De plus, cette mesure peut apporter des garanties importantes pour la sécurité de ces données.”

Actions à logger

Certains actions peuvent être utiles à logger pour de l'audit de sécurité

  • actions destructives (suppression de données/fichiers)
  • auth: tentatives de login, logout, changement/reset de pwd
  • auth: erreurs 401/403
  • upload/download Téléchargement de document
  • exports de données

Côté base de données, un audit log peut être nécessaire. Ex: https://github.com/hasura/audit-trigger ou pgaudit

Upload de fichiers

Risques

  • perte d'intégrité comportement/contenu : remplacement de code applicatif au moyen d'un fichier uploadé / hébergement de contenu illicite (exécution non maîtrisée ou mésinterprétation du contenu)
  • perte de confidentialité : fuite de documents (défaut de contrôle d'accès)
  • perte de disponibilité : déni de service de l'application (défaut de limitation en ressources)

Mesures

  • Upload :
    • Limiter la taille du fichier
    • Vérifier type mime envoyé par le client et le comparer à une liste blanche pré-établie, le stocker, puis refléter la même valeur lors du download du fichier par un utilisateur
    • Si un traitement doit être réalisé, le déporter sur un webservice dédié
  • Stockage :
    • Stocker les fichiers dans un emplacement imposé, hors du document root, dans une partition dédiée, ou en base de données
    • Ne pas utiliser le nom fourni dans les en-têtes HTTP pour le stockage direct du fichier (exemple : utiliser un sha-256 salé sur le nom ou encore un jeton d'accès aléatoire avec lequel la correspondance sera faite en base - ne pas conserver l'extension)
  • Download :
    • Utilisation un contrôleur qui induit un niveau d'abstraction entre la façon de récupérer le fichier et la situation réelle côté serveur. (exemple : GET /download?[random_token] VS GET /static/mnt/volume/fichier.pdf)
    • Utiliser l'en-tête Content-Disposition: attachement afin de forcer le téléchargement
    • Utiliser l'en-tête Content-Type avec la valeur émise lors de l'upload
    • Utiliser l'en-tête X-Content-Type-Options: nosniff afin d'empêcher le navigateur d'inférer le type du fichier et de lui demander de respecter le Content-Type que l'on aura positionné.

FAQ

Mettre à jour les headers HTTP de mes applications

Inspectez les headers HTTP de votre frontend avec Mozilla HTTP Observatory. Vous pouvez également les retrouver sur DashLord.

Plusieurs possibilités pour corriger vos headers :

Directement via l'Ingress

Il est possible d'ajouter des annotations pour forcer les headers directement sur la route de votre application.

Exemple avec kontinuous ou kube-workflow, dans le values.yaml de votre application :

app:
ingress:
annotations:
nginx.ingress.kubernetes.io/configuration-snippet: |
more_set_headers "Content-Security-Policy: default-src 'none'; connect-src 'self' https://*.gouv.fr; font-src 'self'; img-src 'self'; prefetch-src 'self' https://*.gouv.fr; script-src 'self' https://*.gouv.fr; frame-src 'self' https://*.gouv.fr; style-src 'self' 'unsafe-inline'";
more_set_headers "X-Frame-Options: deny";
more_set_headers "X-XSS-Protection: 1; mode=block";
more_set_headers "X-Content-Type-Options: nosniff";

La CSP est à adapter selon vos scripts externes; vous pouvez la définir manuellement ou avec l'extension CSP laboratory.

Le repo template présente l'intégration dans un Next.js statique (branche main) ou avec un serveur (branche hasura).

Côté applicatif

Il est possible d'ajouter les headers côté applicatif, par exemple avec helmet.

Chiffrer des fichiers

Si vous souhaitez chiffrer des fichiers côté serveur, vous pouvez utiliser le module streaming-file-encryption.

La sécurité de ce module repose sur la connaissance nécessaire de 3 informations disctinctes pour pouvoir déchiffrer un fichier :

  • Le mainSecret à définir en tant que variable d'environnement côté applicatif
  • Le context à stocker dans votre base de données et lié à votre fichier
  • Le ciphertext à stocker sur un volume disque persistant

⚠️ il est essentiel de stocker ces données dans des espaces isolés.

Les fichiers chiffrés répondent aux propriétés cryptographiques suivantes :

  • Résistance à la falsification (modification des données)
  • Résistance à la troncature (suppression des données à chaque extrémité ou au milieu)
  • Résistance à l'extension (ajout de données à chaque extrémité ou au milieu)
  • Résistance à la réorganisation (échange de pages de données)

Références

Général

NodeJS

Docker