Conformità FIPS
Gli assembly protetti con Babel Obfuscator funzionano sugli host con FIPS abilitato. Questa pagina spiega come si comportano su questi host le funzionalità di protezione che si basano sulla decifratura in fase di esecuzione, cioè cifratura del codice (MSIL), cifratura delle stringhe, cifratura delle risorse e cifratura di valori e array. Descrive inoltre un problema di avvio che puoi incontrare nei container e come risolverlo: al momento dell’offuscamento, proteggendo l’assembly con l’AES gestito e autonomo di Babel, oppure a livello di ambiente.
Contesto
Molti clienti distribuiscono su host il cui sistema operativo funziona
in modalità FIPS: su Linux questa condizione è esposta come
/proc/sys/crypto/fips_enabled = 1. In questa modalità il provider
crittografico della piattaforma (OpenSSL su Linux) deve eseguire ogni
operazione crittografica tramite un modulo FIPS certificato.
Un problema frequente si presenta quando un container eredita il flag FIPS dell’host ma la sua immagine di base non include un provider FIPS di OpenSSL certificato. OpenSSL 3 tenta allora di entrare in modalità FIPS, non riesce a caricare il modulo del provider FIPS e finisce in uno stato non funzionante in cui falliscono tutte le operazioni di digest e di cifratura, non solo quelle che FIPS normalmente vieterebbe.
Si tratta di una condizione dell’ambiente, non di un difetto
dell’assembly offuscato o di Babel Obfuscator. È documentata
indipendentemente da Babel: vedi
dotnet/dotnet-docker#5849Â
e dotnet/runtime#87884Â .
Riguarda qualsiasi operazione crittografica .NET basata su OpenSSL
nel processo, TLS compreso (HttpClient, gRPC), non solo il codice
offuscato.
Come può manifestarsi negli assembly offuscati
Quando un assembly è protetto con una funzionalità che decifra il contenuto in fase di esecuzione, in particolare la cifratura del codice e la cifratura delle stringhe, Babel inietta un piccolo runtime che usa il provider crittografico della piattaforma per decifrare i corpi dei metodi e le stringhe. Su un host con FIPS non funzionante quel provider è inutilizzabile e, poiché la decifratura viene eseguita all’interno di un inizializzatore di tipo, l’assembly non riesce ad avviarsi prima che venga eseguito qualsiasi tuo codice:
System.TypeInitializationException: The type initializer for '...' threw an exception.
---> Interop+Crypto+OpenSslCryptographicException:
error:0308010C:digital envelope routines::unsupported
at System.Security.Cryptography.AesImplementation.CreateTransform(...)Puoi prevenire questo errore di avvio al momento dell’offuscamento, proteggendo l’assembly con l’algoritmo AES gestito di Babel: il runtime iniettato decifra allora senza il provider crittografico della piattaforma. Vedi più avanti Avviare l’assembly su un host con FIPS non funzionante grazie all’AES gestito. Correggere l’ambiente OpenSSL (descritto anch’esso più avanti) resta comunque consigliato quando hai il controllo del container, perché risolve anche le altre operazioni del processo basate su OpenSSL, come TLS.
Avviare l’assembly su un host con FIPS non funzionante grazie all’AES gestito
Babel Obfuscator può includere nell’assembly protetto un decifratore
AES gestito e autonomo, così il runtime iniettato non dipende più
dal provider crittografico della piattaforma. Proteggi l’assembly con il
valore encryption=aesmanaged dell’opzione
--use:
babel MyApp.dll --msilencryption --use encryption=aesmanagedoppure, in un progetto MSBuild / .babel:
<Use>encryption=aesmanaged</Use>L’offuscatore continua a cifrare con il provider della piattaforma sull’host di build (che funziona correttamente) e produce lo stesso testo cifrato; cambia soltanto il decifratore incluso nell’assembly protetto. In questo modo l’assembly si avvia su un host con FIPS non funzionante senza alcuna modifica all’ambiente. Gli assembly protetti con una versione precedente di Babel devono essere offuscati di nuovo per includere il decifratore gestito.
Prestazioni. Il decifratore gestito lavora a circa 40 MB/s, contro i
diversi GB/s dell’AES della piattaforma con accelerazione hardware. Per
la cifratura delle stringhe, delle risorse e per quella più comune del
codice (con cache) è un costo sostenuto una sola volta all’avvio e non
si nota. Fa eccezione la cifratura del codice su un metodo
contrassegnato con cache=false, il cui corpo viene decifrato a ogni
chiamata: evita di combinare cache=false con encryption=aesmanaged
sui metodi dei percorsi eseguiti più di frequente e mantieni abilitata la cache dei metodi
(l’impostazione predefinita), così la decifratura avviene una volta
sola.
Risolvere il problema nei container
Rendi OpenSSL utilizzabile all’interno del container. Entrambe le opzioni risolvono il sintomo del runtime di offuscamento e ogni altra operazione del tuo processo basata su OpenSSL (per esempio se lo stesso assembly usa anche Babel Licensing o TLS).
Opzione 1: non entrare automaticamente in modalità FIPS nel container
Nelle immagini .NET basate su Ubuntu, OpenSSL rispetta la variabile
d’ambiente OPENSSL_FORCE_FIPS_MODE. Impostala a 0, così OpenSSL non
tenta di entrare in modalità FIPS con un provider che l’immagine non
include.
In un Dockerfile:
ENV OPENSSL_FORCE_FIPS_MODE=0oppure in docker-compose.yml:
environment:
OPENSSL_FORCE_FIPS_MODE: 0Usa questa opzione quando il container in sé non deve essere validato FIPS: l’host resta con FIPS abilitato e solo l’OpenSSL di questo container rinuncia all’attivazione automatica di FIPS, che non funziona.
Opzione 2: usa un’immagine di base con un provider FIPS certificato
Se la conformità FIPS è richiesta all’interno del container, usa un’immagine di base che include un provider FIPS validato, così lo strato crittografico si inizializza correttamente anziché fallire:
- Immagini .NET Azure Linux 3.0
(
mcr.microsoft.com/dotnet/aspnet:8.0-azurelinux3.0e simili), che includono il provider SymCrypt di Microsoft, certificato FIPS, - immagini .NET basate su Red Hat UBI 9
(
registry.access.redhat.com/ubi9/dotnet-90e simili), oppure - Ubuntu Pro con i pacchetti OpenSSL certificati FIPS.
Verificare l’ambiente
Un controllo rapido dall’interno del container ti dice se OpenSSL funziona correttamente:
# Fails on a broken-FIPS host, succeeds once fixed
echo test | openssl dgst -sha1- Errore
error:03000086(o un messaggio che segnala chefips.sonon può essere caricato): OpenSSL è nello stato non funzionante; applica l’opzione 1 o 2. - Una normale riga
SHA1(stdin)= ...: OpenSSL funziona correttamente e gli assembly offuscati si avviano normalmente.
Se non puoi modificare l’ambiente
Se non hai il controllo dell’immagine di base o delle variabili
d’ambiente e il target deve restare rigorosamente vincolato a FIPS,
proteggi l’assembly con l’algoritmo AES gestito descritto sopra
(--use encryption=aesmanaged), così il decifratore iniettato non ha
bisogno del provider della piattaforma. In questo modo un assembly
protetto si avvia anche su un host con OpenSSL non funzionante senza
toccare l’ambiente, e restano disponibili tutte le funzionalità di
decifratura in fase di esecuzione: cifratura del codice, cifratura
delle stringhe, cifratura delle risorse e cifratura di valori e
array. Tieni presente che le altre operazioni dello stesso processo
basate su OpenSSL (come TLS) continueranno a fallire finché l’ambiente
non viene corretto. Contatta l’assistenza se
ti servono indicazioni per una distribuzione specifica.