Conformité FIPS
Les assemblies protégés avec Babel Obfuscator s’exécutent sur les hôtes en mode FIPS. Cette page explique comment se comportent sur ces hôtes les fonctionnalités de protection qui reposent sur un déchiffrement à l’exécution : chiffrement du code (MSIL), chiffrement des chaînes, chiffrement des ressources et chiffrement des valeurs et des tableaux. Elle décrit aussi un problème de démarrage que vous pouvez rencontrer dans les conteneurs et la façon de le résoudre, soit au moment de l’obfuscation, en protégeant l’assembly avec l’AES managé autonome de Babel, soit au niveau de l’environnement.
Contexte
De nombreux clients déploient sur des hôtes dont le système d’exploitation fonctionne en mode FIPS : sous Linux, cet état est exposé par /proc/sys/crypto/fips_enabled = 1. Dans ce mode, le fournisseur cryptographique de la plateforme (OpenSSL sous Linux) est censé traiter chaque opération cryptographique au moyen d’un module FIPS certifié.
Un problème courant survient lorsqu’un conteneur hérite de l’indicateur FIPS de l’hôte alors que son image de base ne contient pas de fournisseur FIPS OpenSSL certifié. OpenSSL 3 tente alors de passer en mode FIPS, ne parvient pas à charger le module du fournisseur FIPS et se retrouve dans un état défaillant où toutes les opérations de hachage et de chiffrement échouent, et pas seulement celles que FIPS interdirait normalement.
Il s’agit d’un état de l’environnement, et non d’un défaut de l’assembly obfusqué ou de Babel Obfuscator. Il est documenté indépendamment de Babel : voir dotnet/dotnet-docker#5849 et dotnet/runtime#87884 . Il touche toute la cryptographie .NET qui repose sur OpenSSL dans le processus, y compris TLS (HttpClient, gRPC), et pas seulement le code obfusqué.
Comment le problème peut se manifester dans les assemblies obfusqués
Lorsqu’un assembly est protégé par une fonctionnalité qui déchiffre du contenu à l’exécution, en particulier le chiffrement du code et le chiffrement des chaînes, Babel injecte un petit runtime qui utilise le fournisseur cryptographique de la plateforme pour déchiffrer les corps de méthode et les chaînes. Sur un hôte en mode FIPS défaillant, ce fournisseur est inutilisable ; comme le déchiffrement s’exécute dans un initialiseur de type, l’assembly ne parvient pas à démarrer, avant même que votre code ne s’exécute :
System.TypeInitializationException: The type initializer for '...' threw an exception.
---> Interop+Crypto+OpenSslCryptographicException:
error:0308010C:digital envelope routines::unsupported
at System.Security.Cryptography.AesImplementation.CreateTransform(...)Vous pouvez éviter cet échec au démarrage au moment de l’obfuscation en protégeant l’assembly avec l’algorithme AES managé de Babel : le runtime injecté déchiffre alors sans le fournisseur cryptographique de la plateforme (voir Démarrer sur un hôte en mode FIPS défaillant avec l’AES managé ci-dessous). Corriger l’environnement OpenSSL (également décrit ci-dessous) reste recommandé lorsque vous maîtrisez le conteneur, car cela résout en plus les autres opérations du processus qui reposent sur OpenSSL, comme TLS.
Démarrer sur un hôte en mode FIPS défaillant avec l’AES managé
Babel Obfuscator peut placer un déchiffreur AES managé autonome dans l’assembly protégé, de sorte que le runtime injecté ne dépend plus du fournisseur cryptographique de la plateforme. Protégez l’assembly avec la valeur encryption=aesmanaged de l’option --use :
babel MyApp.dll --msilencryption --use encryption=aesmanagedou, dans un projet MSBuild ou .babel :
<Use>encryption=aesmanaged</Use>L’obfuscateur chiffre toujours avec le fournisseur de la plateforme sur l’hôte de build (qui est sain) et produit le même texte chiffré ; seul le déchiffreur placé dans l’assembly protégé change. L’assembly peut ainsi démarrer sur un hôte en mode FIPS défaillant sans aucune modification de l’environnement. Les assemblies protégés avec une version antérieure de Babel doivent être obfusqués de nouveau pour recevoir le déchiffreur managé.
Performances. Le déchiffreur managé traite environ 40 Mo/s, contre plusieurs Go/s pour l’AES de la plateforme, accéléré par le matériel. Pour le chiffrement des chaînes, des ressources et du code dans le cas courant (avec cache), il s’agit d’un coût unique au démarrage, qui passe inaperçu. L’exception est le chiffrement du code sur une méthode marquée cache=false, dont le corps est déchiffré à chaque appel : évitez de combiner cache=false et encryption=aesmanaged sur les méthodes critiques pour les performances, et laissez le cache des méthodes activé (valeur par défaut) pour que le déchiffrement n’ait lieu qu’une fois.
Résoudre le problème dans les conteneurs
Rendez OpenSSL utilisable dans le conteneur. L’une ou l’autre option corrige le symptôme du runtime d’obfuscation et toutes les autres opérations de votre processus qui reposent sur OpenSSL (par exemple si le même assembly utilise aussi Babel Licensing ou TLS).
Option 1Â : ne pas passer automatiquement en mode FIPS dans le conteneur
Sur les images .NET basées sur Ubuntu, OpenSSL tient compte de la variable d’environnement OPENSSL_FORCE_FIPS_MODE. Réglez-la sur 0 pour qu’OpenSSL ne tente pas de passer en mode FIPS avec un fournisseur que l’image ne contient pas.
Dans un Dockerfile :
ENV OPENSSL_FORCE_FIPS_MODE=0ou dans docker-compose.yml :
environment:
OPENSSL_FORCE_FIPS_MODE: 0Utilisez cette option lorsque le conteneur lui-même n’a pas besoin d’être validé FIPS : l’hôte reste en mode FIPS et seul l’OpenSSL de ce conteneur renonce au passage automatique en mode FIPS, qui est défaillant.
Option 2 : utiliser une image de base dotée d’un fournisseur FIPS certifié
Si la conformité FIPS est exigée dans le conteneur, utilisez une image de base qui contient un fournisseur FIPS validé, afin que la couche cryptographique s’initialise correctement au lieu d’échouer :
- les images .NET Azure Linux 3.0 (
mcr.microsoft.com/dotnet/aspnet:8.0-azurelinux3.0et images similaires), qui contiennent le fournisseur SymCrypt de Microsoft, certifié FIPS, - les images .NET basées sur Red Hat UBI 9 (
registry.access.redhat.com/ubi9/dotnet-90et images similaires), ou - Ubuntu Pro avec les paquets OpenSSL certifiés FIPS.
Vérifier l’environnement
Une vérification rapide depuis l’intérieur du conteneur indique si OpenSSL est sain :
# Fails on a broken-FIPS host, succeeds once fixed
echo test | openssl dgst -sha1- Échec avec
error:03000086(ou un message indiquant quefips.sone peut pas être chargé) : OpenSSL est dans l’état défaillant ; appliquez l’option 1 ou 2. - Une ligne
SHA1(stdin)= ...normale : OpenSSL est sain et les assemblies obfusqués démarrent normalement.
Si vous ne pouvez pas modifier l’environnement
Si vous ne maîtrisez ni l’image de base ni les variables d’environnement et que la cible doit rester strictement soumise aux contraintes FIPS, protégez l’assembly avec l’algorithme AES managé décrit ci-dessus (--use encryption=aesmanaged), afin que le déchiffreur injecté n’ait pas besoin du fournisseur de la plateforme. Un assembly protégé peut ainsi démarrer même sur un hôte dont OpenSSL est défaillant, sans toucher à l’environnement, et toutes les fonctionnalités de déchiffrement à l’exécution (chiffrement du code, chiffrement des chaînes, chiffrement des ressources et chiffrement des valeurs et des tableaux) restent disponibles. Notez que les autres opérations du même processus qui reposent sur OpenSSL (comme TLS) continueront d’échouer tant que l’environnement n’aura pas été corrigé. Contactez l’assistance si vous avez besoin de conseils pour un déploiement précis.