Memorizzazione di segreti nelle app Windows: evitare la configurazione in testo normale con DPAPI

· Aggiornato il: · · Sviluppo Windows, Sicurezza, DPAPI, C# / .NET, Win32

Nel post precedente, “Una checklist di sicurezza minima per Windows Application Development”, abbiamo tracciato la linea di base: “non inserire segreti nel codice sorgente o nella configurazione del testo normale” e “su Win32 / .NET, utilizzare DPAPI / ProtectedData”.

Questa volta scaviamo un po’ più a fondo in una parte: “usare DPAPI per essere almeno migliore del testo normale.”

L’obiettivo sono Windows app come queste:

  • Applicazioni desktop WPF / WinForms / WinUI
  • Clienti C# / .NET Windows
  • Le app sono tentate di archiviare credenziali di connessione o token API nei file di configurazione locali

Ciò che trattiamo qui è un progetto realistico per “non lasciare segreti che devono essere archiviati localmente in testo non crittografato in appsettings.json.” Questa non è una storia sulla “difesa perfetta che batte qualsiasi attaccante”. Se lo si esagera, le chiacchiere sulla sicurezza si trasformeranno rapidamente in una storia di fantasmi.

1. Prima la conclusione

In pratica, questo ordine di pensiero è il più chiaro.

  1. In primo luogo, non rivelare al cliente segreti di lunga durata
    • Preferisci l’autenticazione Windows, l’autenticazione integrata, l’accesso utente interattivo e la gestione dei segreti lato server
  2. Se il storage locale è veramente necessario, non memorizzarlo in testo normale
    • Il Windows, imposta DPAPI / ProtectedData come primo candidato
  3. Per le normali app desktop, il valore predefinito è DataProtectionScope.CurrentUser
    • LocalMachine ha casi d’uso piuttosto limitati
  4. DPAPI non ti protegge fino al punto in cui “la macchina è completamente compromessa”
    • Il codice eseguito con gli stessi privilegi utente può, fondamentalmente, decrittografare tutto ciò che l’utente può decrittografare

E il punto più importante di questo articolo è qui:

“La chiave segreta deve comunque essere conservata da qualche parte, quindi il testo in chiaro e DPAPI non sono la stessa cosa dal punto di vista della sicurezza?”

Questo è giusto per metà, ma la conclusione è sbagliata.

  • Roll-your-own AES con la chiave memorizzata nella stessa app o nella stessa configurazione è, infatti, vicino al testo normale
  • Ma DPAPI delega la gestione delle chiavi all’OS e vincola la parte che può decrittografare a “quell’utente Windows” o “quel computer”
  • Di conseguenza, la resistenza contro incidenti come un file di configurazione trapelato da solo, il file trasferito su un altro PC, allegati inviati erroneamente, perdite di backup o contaminazione del repository cambia radicalmente

In altre parole: anche se l’affermazione astratta “la chiave è da qualche parte” li fa sembrare uguali, “chi può usarlo, in quale contesto e con quanta facilità” è completamente diverso.

Chiamare “una chiave sotto lo zerbino” e “una chiave consegnata alla reception dopo la verifica dell’identità” è la stessa cosa un po’ approssimativa.

2. Perché la configurazione del testo in chiaro è pericolosa

Il motivo per cui il testo in chiaro storage è pericoloso è molto più banale della teoria crittografica. In pratica le perdite avvengono attraverso percorsi come questi:

  • Il file di configurazione viene assegnato a Git così com’è
  • Il file di configurazione finisce all’ingrosso in una risoluzione dei problemi ZIP
  • Una richiesta di supporto viene fornita con il file di configurazione allegato
  • Terze parti possono leggerlo tramite backup o condivisioni di file
  • Le stringhe di connessione e i token vengono visualizzati testualmente nei log
  • Un dipendente in partenza o un altro utente può leggere il file sulla stessa macchina

Il problema più grande con il testo in chiaro è che nel momento in cui viene letto, diventa un segreto.

  • Apri il file ed è finito
  • Copialo ed è finita
  • Allegalo a un’e-mail e il gioco è fatto
  • Lascialo in un deposito e fai da babysitter a lui in modo semi-permanente

L’aggressore non ha nemmeno bisogno di essere sofisticato. “Si apre in un editor di testo” è, di per sé, piuttosto debole.

3. La risposta alla domanda “La chiave è comunque conservata da qualche parte, quindi non è la stessa cosa?”

Questa domanda è ragionevole. E rispondere in modo approssimativo fa sì che gli articoli sulla sicurezza diventino improvvisamente vaghi.

La risposta: sì nel senso che “ci vuole una chiave da qualche parte”; no nel senso che “dunque sono la stessa cosa”.

3.1. Cosa è uguale e cosa è diverso

In effetti, la crittografia alla fine necessita di una radice di fiducia. I segreti non nascono gratuitamente da qualche parte nell’universo. È un mondo duro in questo modo.

Tuttavia, la differenza di sicurezza è determinata da questi tre punti:

  • L’app conserva direttamente la chiave?
  • A quale soggetto è vincolata la chiave?
  • Se viene rubato solo il file, è possibile decifrarlo?

Una tabella approssimativa delle differenze:

Metodo Il file di configurazione viene letto Solo il file viene trasferito su un altro PC Letto da un altro utente sullo stesso PC Codice in esecuzione con gli stessi privilegi utente
Testo in chiaro Perdite sul posto Perde così com’è Perde così com’è Leggibile, ovviamente
Crea la tua crittografia + chiave nella stessa configurazione / binario Per lo più perdite Per lo più perdite Per lo più perdite Decifrabile, ovviamente
DPAPI + CurrentUser Non immediatamente leggibile solo dal file Normalmente difficile da decifrare Normalmente difficile da decifrare Decifrabile
DPAPI + LocalMachine Non immediatamente leggibile dal solo file Normalmente è difficile da decifrare ovunque tranne che sul PC Ampiamente decifrabile sullo stesso PC Decifrabile

Il punto importante qui è che DPAPI separa “può leggere il file” da “può usare il segreto”.

Con il testo in chiaro, questi due sono la stessa cosa. Se il file può essere letto, lo stesso vale per il segreto.

Ma con DPAPI – almeno con CurrentUser – la decrittazione deve avvenire:

  • come utente Windows
  • in quel contesto Windows
  • attraverso il macchinario di protezione OS

Nella scena di un incidente reale, questa differenza è sostanziale.

3.2. “Ma lo stesso utente può comunque decifrarlo, giusto?” — Esatto

Questo è un punto che dovrebbe essere scritto senza coperture.

Il codice eseguito con gli stessi privilegi utente può, fondamentalmente, decrittografare tutto ciò che l’utente può decrittografare.

In altre parole, DPAPI non è rivolto principalmente a situazioni come:

  • La macchina è già compromessa da malware
  • L’attaccante può eseguire il codice come quell’utente
  • La macchina è stata completamente rilevata a livello di amministratore

In tali situazioni, poiché l’app stessa può decrittografare, anche il codice dell’aggressore può decrittografarsi. “Ma è crittografato” non è molto rassicurante in questo caso.

Dove DPAPI aiuta è principalmente il lato “perdita di file / spostamento errato / esfiltrazione offline / accesso da parte di un altro utente”.

Ottieni questo al contrario e ottieni entrambi:

  • sottovalutare ciò che può proteggere e non utilizzarlo
  • sopravvalutare ciò che non può proteggere e sentirsi al sicuro

Entrambi sono silenziosamente pericolosi.

3.3. Quindi qual è il vantaggio reale?

I vantaggi di DPAPI in una frase:

“Puoi disaccoppiare il segreto stesso dalla leggibilità del file di configurazione.”

Ad esempio, testo in chiaro e DPAPI differiscono in incidenti come questi:

  • Un utente ha inviato il file di configurazione all’assistenza
  • Il file di configurazione è finito in una risoluzione dei problemi ZIP
  • Solo il file di configurazione è trapelato da un backup
  • È stato copiato in una cartella condivisa
  • Gli sviluppatori potevano guardare solo il testo cifrato e non leggerne il contenuto

Questi sono vantaggi abbastanza realistici. Puoi ridurre il raggio dell’incidente quotidiano senza considerare l’aggressore come un sovrumano del film.

4. Perché DPAPI è perfetto

Quando si gestiscono i segreti archiviati localmente su Windows, i motivi per cui DPAPI raggiunge il punto pratico sono i seguenti.

4.1. La gestione delle chiavi può essere delegata all’OS

Genera tu stesso una chiave AES, archiviala, imposta le autorizzazioni su di essa, ruotala, pensa al raggio di esplosione di una perdita e aggiungi il rilevamento delle manomissioni. Questo è più pesante di quanto sembri. E fatto in modo approssimativo, di solito finisce con la chiave posizionata nella stessa posizione.

Con DPAPI, il problema “come creiamo la chiave di crittografia e dove la mettiamo” può essere rimosso dall’implementazione dell’app.

In questo senso, è più vicino all’essenza vedere DPAPI come “un’API per delegare la gestione delle chiavi a OS,” non “un’API per scegliere un algoritmo di crittografia.”

4.2. La parte che effettua la decrittazione può essere legata a un utente Windows o al computer

Per una normale app desktop, scegliere CurrentUser è spesso la scelta giusta.

La decrittazione si basa su:

  • quell’utente ha effettuato l’accesso
  • l’operazione in esecuzione nel contesto di quell’utente

Di conseguenza, si ottiene la proprietà che copiare solo il testo cifrato su un altro PC non lo rende facilmente utilizzabile.

4.3. Il rilevamento delle manomissioni avviene facilmente

Un errore comune con la crittografia roll-your-own è dichiarando “abbiamo crittografato con AES, fatto” e dimenticando il rilevamento delle manomissioni.

DPAPI fornisce anche protezione dell’integrità sui dati crittografati, quindi rilevare quando qualcuno riscrive il testo cifrato può viaggiare sul macchinario OS: un vantaggio pratico.

4.4. Facile da usare da C# / .NET

In C# puoi utilizzare direttamente System.Security.Cryptography.ProtectedData. Non dover aggiungere librerie aggiuntive è un vero aiuto per le app solo per Windows.

5. Cosa DPAPI protegge e cosa no

È più sicuro tracciare chiaramente questa linea.

5.1. Cosa diventa più facile da proteggere

DPAPI è efficace almeno in scenari come questi:

  • Perdita di testo in chiaro dei file di configurazione
  • File trasferiti su un altro PC
  • Accesso da parte di un altro utente sullo stesso PC (assumendo CurrentUser)
  • Perdita tramite backup o allegati
  • Lo stato “spiacenti, chiunque può leggerlo” nelle impostazioni di sviluppo e manutenzione

5.2. Ciò che non può proteggere o protegge debolmente

D’altra parte, non fidarti troppo in queste situazioni:

  • Codice di attacco eseguito con gli stessi privilegi utente
  • Compromissione totale della macchina stessa
  • Subentro con privilegi di amministratore
  • Il testo in chiaro in memoria dopo la decrittografia dell’app
  • Segreti di lunga durata distribuiti in modo identico a tutti i client

L’ultimo – “un segreto di lunga durata condiviso da tutti i clienti” – è particolarmente importante.

Ad esempio, disegni come:

  • incorporare la stessa chiave API per tutti i clienti
  • avere la stessa password condivisa su tutte le macchine
  • spedire una chiave di decrittazione fissa che risiede interamente sul client

tendono ad essere progetti in cui l’estrazione da ogni singola macchina si estende a tutto.

DPAPI è efficace nel “rendere la posizione storage migliore del testo in chiaro”, ma non giustifica segreti che non dovrebbero essere presenti sul client in primo luogo.

6. Scelta tra CurrentUser e LocalMachine

Questa parte conta molto. Scegli con noncuranza e il significato cambia.

6.1. L’impostazione predefinita è CurrentUser

Per un’applicazione desktop Windows ordinario, iniziare da CurrentUser.

Buone misure:

  • App desktop WPF / WinForms / WinUI rivolte all’utente
  • App con impostazioni e credenziali per utente
  • App che mantengono le impostazioni in %LocalAppData% o %AppData%

In questo caso, diventa naturale trattare i dati come “il segreto di quell’utente Windows.”

6.2. LocalMachine Ha casi d’uso piuttosto limitati

LocalMachine sembra conveniente, ma per una normale app desktop è troppo ampio.

È adatto a casi come:

  • Un servizio Windows su una macchina affidabile e monouso
  • Segreti utilizzati solo da processi specifici su quella macchina
  • Casi che devono effettivamente funzionare tra gli utenti che accedono sullo stesso computer

Ma le avvertenze sono pesanti.

  • Ampiamente decifrabile dai processi in esecuzione su quel PC
  • Tende a diventare pericoloso su macchine condivise, RDS, host jump e ambienti multiutente
  • Sceglierlo perché “tutti possono usarlo, quindi è facile” di solito causa problemi in seguito

6.3. In caso di dubbio, pensa in questo modo

  • App UI ordinaria -> CurrentUser
  • Un caso speciale che deve davvero essere protetto per macchina -> LocalMachine
  • Qualsiasi utente deve essere in grado di decrittografare, ma sulla macchina sono presenti altri utenti -> di solito è meglio rivisitare il design stesso

6.4. Servizi e furto d’identità Aggiungi cautela

Aggiungi i servizi Windows o la rappresentazione nel mix e il significato di CurrentUser diventa un po’ più pesante.

  • Chi è il conto di esecuzione?
  • Il profilo di quell’account è caricato?
  • In quale contesto avviene la decrittazione?

Se questi vengono disallineati, ti ritroverai facilmente con “è stato crittografato correttamente ma non verrà decrittografato.” Per gli scenari di servizio, “usa semplicemente CurrentUser” a volte non è sufficiente.

6.5. Casi operativi in cui “non si riesce più a decrittare”

Nella pratica, fa più male l’incidente “non riesco più a decrittare” della crittografia stessa. Poiché DPAPI lega la possibilità di decrittare a un utente Windows o a un computer, quando quel legame si rompe i dati diventano illeggibili.

Le 5 situazioni da conoscere in anticipo sono queste.

Caso Cosa succede Come prepararsi
Reimpostazione della password da amministratore La protezione collegata alla password dell’utente può venire meno, quindi i dati protetti da DPAPI possono diventare inaccessibili. Microsoft documenta questo scenario come possibile effetto della reimpostazione amministrativa Progettare il recupero del segreto: in caso di fallimento della decrittazione, reindirizzare all’immissione
Ricreazione del profilo Un nuovo profilo ha materiale chiave diverso, quindi i testi cifrati precedenti non possono più essere decifrati Versionare il file di configurazione e non trattare il fallimento di decrittazione come errore fatale
Copia del testo cifrato su un altro PC Poiché la chiave risiede nel profilo utente, portare solo il ciphertext CurrentUser su un altro PC non basta (questo è il rovescio del vantaggio citato in 3.1) Progettare in modo che il segreto venga ripetutamente salvato per ogni dispositivo
Profilo roaming In questo caso si legge. Poiché il profilo e il materiale chiave si spostano insieme, Microsoft Learn afferma esplicitamente che un utente con profilo roaming può decrittare da un altro computer. Non assumere “altro PC = non leggo”, ma verificare se il roaming è attivo prima di decidere la procedura di migrazione.
Modifica dell’account di esecuzione di un servizio Se l’account che protegge e quello che decrittano cambiano, CurrentUser non funziona più Includere una procedura di riprotezione quando si cambia account

In sintesi, si scrive il codice assumendo che ProtectedData.Unprotect può fallire. Il fallimento genera una CryptographicException, che va gestita per reindirizzare all’immissione.

using System;
using System.Security.Cryptography;
using System.Text;

// protectedBase64: testo cifrato letto dal file di configurazione (Base64)
// entropy: stesso valore usato al momento della protezione, null se non usato
static bool TryUnprotect(string protectedBase64, byte[]? entropy, out string plaintext)
{
    plaintext = string.Empty;

    try
    {
        byte[] plainBytes = ProtectedData.Unprotect(
            Convert.FromBase64String(protectedBase64),
            optionalEntropy: entropy,
            scope: DataProtectionScope.CurrentUser);

        plaintext = Encoding.UTF8.GetString(plainBytes);
        return true;
    }
    catch (CryptographicException)
    {
        // Impossibile decrittare: molto probabilmente l'ambiente è cambiato.
        // Non terminare; lasciare che il chiamante reindirizzi all'immissione.
        return false;
    }
    catch (FormatException)
    {
        // Il Base64 è corrotto
        return false;
    }
}

La maggior parte delle segnalazioni “ho crittografato ma non riesco a decrittare” ricade in una di queste righe.

7. Linee guida minime di attuazione

Se tutto ciò che desideri è “smettere di avere testo in chiaro nel file di configurazione” in un’app Windows, la progettazione non deve essere molto complessa. Ma ci sono alcuni punti da non perdere.

7.1. Proteggi solo i segreti

Invece di crittografare l’intera configurazione in blocco, è più semplice gestirla proteggendo prima solo gli elementi segreti.

Ad esempio, dividilo in questo modo. Questi possono spesso rimanere in testo normale:

  • Server URL
  • Nome utente
  • Nome della banca dati
  • Flag di funzionalità

Considerando che questi sono obiettivi di protezione:

  • Password
  • Token API
  • Aggiorna i token
  • Credenziali della cartella condivisa

Con questa suddivisione ottieni:

  • modifica più semplice della configurazione
  • revisione delle differenze più semplice
  • chiarezza su cosa è segreto
  • operazioni complessive più semplici

7.2. Per impostazione predefinita, la posizione Storage è per utente

Per un’app desktop ordinaria, imposta per impostazione predefinita la posizione storage su una posizione per utente.

  • %LocalAppData%\Vendor\App\settings.json
  • %AppData%\Vendor\App\settings.json

Come minimo, non inserirlo casualmente nella cartella di installazione o in un posto facilmente condiviso.

Anche con la protezione DPAPI, se il ACLs nella posizione storage è approssimativo, ti ritroverai con “il testo cifrato viene letto”, “la struttura di configurazione è visibile”, “si verificano errori operativi”. La difesa funziona meglio a livelli, non a livello singolo.

7.3. optionalEntropy Non è una seconda chiave onnipotente

ProtectedData ti permette di superare optionalEntropy. Questo è utile, ma non è “una seconda chiave magica che ti rende sicuro se la incorpori nel codice binario.”

  • Mettilo nello stesso file e non è un segreto
  • Incorporalo come valore fisso nel binario e non è nemmeno un segreto
  • Anche così, è utile per l’identificazione dello scopo e la prevenzione degli abusi

In pratica, passando una sequenza di byte fissa costruita da:

  • il nome dell’applicazione
  • il nome dello scopo
  • un identificatore di versione

e usarlo “per evitare di accettare accidentalmente testo cifrato da uno scopo diverso” è al livello giusto.

7.4. Ciò non significa che il testo cifrato possa entrare in Git

Anche questo è tranquillamente importante.

DPAPI il testo cifrato è molto migliore del testo in chiaro, ma Ciò non significa che il file di configurazione possa finire nel repository.

Le ragioni sono semplici:

  • Il testo cifrato vive a lungo
  • La stessa macchina o lo stesso contesto potrebbero un giorno essere riprodotti
  • Il file contiene informazioni oltre al segreto
  • Si radica la cultura del “è protetto, quindi possiamo essere negligenti”.

“Meglio del testo semplice” e “sicuro ovunque lo metti” sono cose completamente diverse.

7.5. Non inserirlo nei registri

Un modello sorprendentemente comune sta rovinando tutto registrando il valore dopo la decrittazione.

  • Dumping dell’intera stringa di connessione in caso di errore di connessione
  • Lasciare l’intestazione Authorization nei log su un’API 401
  • Miscelazione di segreti in messaggi di eccezione

Esegui queste operazioni e, anche dopo aver eliminato i file di configurazione in chiaro, i tuoi registri diventeranno invece il magazzino di testo in chiaro. Triste, ma molto reale.

8. Un’implementazione minima di C# / .NET

8.1. Prima, devi aggiungere il riferimento

ProtectedData sembra fare parte della BCL, ma da quale assembly provenga dipende dal target framework. Se non si fa questo passaggio, il tipo ProtectedData non si risolve.

Target Operazione necessaria Fonte
.NET Framework Aggiungere il riferimento assembly System.Security System.Security.dll
.NET Core / .NET 5 e successivi (inclusi .NET 6 / 8) Aggiungere il pacchetto NuGet System.Security.Cryptography.ProtectedData System.Security.Cryptography.ProtectedData.dll

Questo pacchetto non è incluso in alcun shared framework per .NET Core / .NET 5 e successivi. Anche per target Windows come net8.0-windows, il riferimento deve essere esplicito.

dotnet add package System.Security.Cryptography.ProtectedData

C’è un altro punto da sapere prima di implementare. ProtectedData è specifico di Windows. Poiché si basa su DPAPI, se viene chiamato su .NET in esecuzione su piattaforme diverse da Windows, viene generata un’eccezione PlatformNotSupportedException. Se la base di codice è multipiattaforma, pianifica un’alternativa fin dall’inizio come descritto in 10.1.

Di seguito è riportato un esempio minimo di protezione di una stringa salvata in un file di configurazione con CurrentUser. Include un optionalEntropy fisso per l’identificazione dello scopo: non considerarlo come una chiave segreta.

using System;
using System.Security.Cryptography;
using System.Text;

public static class DpapiSecretProtector
{
    // For purpose identification. Not a second secret key.
    private static readonly byte[] Entropy =
        Encoding.UTF8.GetBytes("ComComponent:DesktopApp:SettingsSecret:v1");

    public static string ProtectToBase64(string plaintext)
    {
        ArgumentNullException.ThrowIfNull(plaintext);

        byte[] plainBytes = Encoding.UTF8.GetBytes(plaintext);
        byte[] protectedBytes = Array.Empty<byte>();

        try
        {
            protectedBytes = ProtectedData.Protect(
                plainBytes,
                optionalEntropy: Entropy,
                scope: DataProtectionScope.CurrentUser);

            return Convert.ToBase64String(protectedBytes);
        }
        finally
        {
            Array.Clear(plainBytes, 0, plainBytes.Length);

            if (protectedBytes.Length > 0)
            {
                Array.Clear(protectedBytes, 0, protectedBytes.Length);
            }
        }
    }

    public static string UnprotectFromBase64(string protectedBase64)
    {
        ArgumentNullException.ThrowIfNull(protectedBase64);

        byte[] protectedBytes = Convert.FromBase64String(protectedBase64);
        byte[] plainBytes = Array.Empty<byte>();

        try
        {
            plainBytes = ProtectedData.Unprotect(
                protectedBytes,
                optionalEntropy: Entropy,
                scope: DataProtectionScope.CurrentUser);

            return Encoding.UTF8.GetString(plainBytes);
        }
        finally
        {
            Array.Clear(protectedBytes, 0, protectedBytes.Length);

            if (plainBytes.Length > 0)
            {
                Array.Clear(plainBytes, 0, plainBytes.Length);
            }
        }
    }
}

L’utilizzo è semplice.

string protectedPassword = DpapiSecretProtector.ProtectToBase64(password);

// Save to JSON etc.
// settings.DbPasswordProtected = protectedPassword;

string password = DpapiSecretProtector.UnprotectFromBase64(settings.DbPasswordProtected);

Il file di configurazione può quindi assumere una forma come questa.

{
  "ApiBaseUrl": "https://api.example.com/",
  "UserName": "app-user",
  "PasswordProtected": "AQAAANCMnd8BFdERjHoAwE..."
}

Le cose belle di questo modulo:

  • Il URL e il nome utente possono essere modificati normalmente
  • Solo la password è protetta
  • La struttura della configurazione è di facile lettura
  • Meno incline agli incidenti rispetto a lasciarlo in chiaro

9. Progetti ancora pericolosi

Anche con DPAPI in uso, i seguenti progetti sono ancora pericolosi.

9.1. Portare in giro il valore decriptato per molto tempo

Vuoi evitare di prendere il valore decrittografato e:

  • inserendolo nei log
  • mettendolo sullo schermo
  • includendolo nelle eccezioni
  • lasciandolo parcheggiato su oggetti longevi

“Crittografato a riposo” e “sicuro durante l’uso” sono problemi separati.

9.2. Dare ad ogni installazione un segreto comune

Ad esempio, un progetto in cui ogni utente possiede la stessa chiave API è non risolto fondamentalmente memorizzandolo con DPAPI.

Perché se l’app riesce a decrittografarlo su ogni singola macchina, il segreto può essere estratto lì.

Segreti di questo tipo dovrebbero essere indirizzati verso:

  • mantenendoli sul lato server
  • far sì che il cliente detenga solo token
  • credenziali per utente
  • gettoni in scadenza

9.3. Scegliere LocalMachine perché “è facile”

Questo è davvero comune.

Sei tentato di scegliere LocalMachine perché:

  • continua a leggere anche dopo aver cambiato utente
  • Anche i servizi possono leggerlo
  • è conveniente se funziona

Ma per una normale app desktop, it estende la decrittabilità ad altri processi su quel PC, il che ne cambia notevolmente il significato.

9.4. Aggiungere criptovalute personalizzabili e sentirsi al sicuro

Invece di DPAPI, implementazioni come:

  • incorporando una chiave AES nel codice sorgente
  • inserendo la chiave AES in un altro campo del file di configurazione
  • trattare una “stringa leggermente offuscata” come una chiave

solitamente hanno scarso effetto.

Tra “testo non in chiaro” e “sicuro” c’è un abisso piuttosto ampio.

10. Casi in cui DPAPI non è sufficiente

DPAPI è utile, ma non onnipotente. Considera altre opzioni nei seguenti casi.

10.1. Desideri eseguire su piattaforme diverse da Windows

DPAPI / ProtectedData sono per Windows. Un’app multipiattaforma non può essere costruita su questo presupposto.

10.2. Vuoi lo stesso segreto su più macchine e utenti

Requisiti come la decrittografia dello stesso testo cifrato su più PCs o la condivisione tra più utenti, non rientrano nei limiti di DPAPI, che si lega a “quella macchina, quell’utente”.

In tal caso, considera i progetti che soddisfano i requisiti:

  • gestione dei segreti lato server
  • un’infrastruttura di credenziali
  • Autenticazione Windows / autenticazione integrata
  • un archivio credenziali per l’applicazione

10.3. Ciò che viene archiviato è la stessa credenziale utente

Per le app desktop in pacchetto / WinUI, se l’elemento archiviato è chiaramente una coppia di:

  • nome utente
  • parola d’ordine

allora anche il Credential Locker è un’opzione. Ma il focus di questo articolo rimane la pratica base DPAPI per “arrestare i file di configurazione di testo normale sui client Windows.”

11. Priorità consigliate nella pratica

Infine, in caso di dubbio nella pratica, pensare in questo ordine mantiene le cose organizzate.

Priorità 1: non trattenerlo affatto

  • Autenticazione Windows
  • Autenticazione integrata
  • Accesso interattivo
  • Mantenimento segreto lato server
  • Gettoni di breve durata

Priorità 2: passare ai segreti per utente

  • Per utente su segreti condivisi
  • Token rinnovabili su credenziali fisse di lunga durata
  • Evitare chiavi comuni a tutti i client

Priorità 3: se è necessario il storage locale, DPAPI

  • Normalmente CurrentUser
  • Posizione storage per utente
  • Proteggi solo gli oggetti segreti
  • Tienilo lontano dai registri

Priorità 4: Tratta LocalMachine come eccezione

  • È davvero necessario che sia per macchina?
  • Altri utenti accedono mai a quella macchina?
  • È valido come progettazione di servizi?

Quando un’app Windows deve archiviare informazioni sensibili in un file di configurazione, vuoi evitare di lasciarlo in chiaro.

E alla domanda:

“La chiave viene comunque memorizzata da qualche parte, quindi non è la stessa cosa?”

la risposta pratica è questa:

  • La crittografia personalizzata con la chiave nello stesso posto è, per lo più, la stessa cosa
  • DPAPI non è la stessa cosa
    • La gestione delle chiavi può essere delegata all’OS
    • La parte che esegue la decrittografia può essere associata a un utente / computer Windows
    • La fuga del file di per sé non equivale più alla fuga del segreto
  • Tuttavia, non risolve:
    • codice in esecuzione con gli stessi privilegi utente
    • una macchina completamente compromessa
    • Segreti condivisi di lunga durata che non dovrebbero essere sul client in primo luogo

In breve, DPAPI non è un muro di fortezza onnipotente. Ma ha più o meno l’effetto di sostituire il riquadro della finestra spalancato che è un file di configurazione in chiaro con almeno una finestra decente.

Nel lavoro reale con i clienti Windows, questa differenza è sostanziale. Iniziare facendo in modo che questa parte sia corretta è la mossa più realistica.

13. Riferimenti

  • Articolo precedente: https://comcomponent.com/it/blog/2026/03/14/001-windows-app-security-minimum-checklist/
  • Microsoft Learn: CryptProtectData https://learn.microsoft.com/en-us/windows/win32/api/dpapi/nf-dpapi-cryptprotectdata
  • Microsoft Learn: ProtectedData https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.protecteddata?view=windowsdesktop-10.0
  • Microsoft Learn: DataProtectionScope https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.dataprotectionscope?view=windowsdesktop-10.0
  • Microsoft Learn: Come: utilizzare la protezione dei dati https://learn.microsoft.com/en-us/dotnet/standard/security/how-to-use-data-protection
  • Microsoft Learn: Credential locker per le app Windows https://learn.microsoft.com/en-us/windows/apps/develop/security/credential-locker

Articoli recenti con gli stessi tag per approfondire argomenti vicini.

Queste pagine collocano l’argomento in un contesto più ampio di servizi e decisioni.

L’articolo è direttamente collegato ai servizi seguenti.

Sviluppo di applicazioni Windows

Questo argomento tocca la progettazione generale di un'app Windows (come vengono archiviate le credenziali, dove risiedono le impostazioni per utente e cosa viene inserito nei log), quindi si adatta bene al nostro servizio di sviluppo di applicazioni Windows.

Domande frequenti

Domande che ricorrono nelle consulenze sull’argomento dell’articolo.

DPAPI non è la stessa cosa del testo in chiaro, dal momento che la chiave deve comunque essere memorizzata da qualche parte?
No. Il roll-your-own AES con la chiave memorizzata nella stessa app o file di configurazione è in effetti simile al testo normale, ma DPAPI delega la gestione delle chiavi all'OS e vincola la parte che può decrittografare a quell'utente Windows o a quel computer. Questo separa "può leggere il file" da "può usare il segreto": se solo il file di configurazione viene divulgato - tramite un backup, un allegato inviato erroneamente, una risoluzione dei problemi ZIP o viene trasportato su un altro PC - il testo cifrato da solo non è facilmente utilizzabile. Ciò che determina la differenza di sicurezza è chi può utilizzare la chiave, in quale contesto e con quanta facilità.
Dovrei usare DataProtectionScope.CurrentUser o LocalMachine?
Per una normale app desktop, l'impostazione predefinita è CurrentUser: la decrittografia richiede quindi il contesto dell'utente Windows, quindi la copia del testo cifrato su un altro PC o sull'account di un altro utente non lo rende facilmente utilizzabile. LocalMachine è ampiamente decrittografabile dai processi in esecuzione su quel PC, il che diventa pericoloso su macchine condivise, host RDS e ambienti multiutente, quindi i suoi casi d'uso sono piuttosto limitati, principalmente servizi Windows su macchine affidabili monouso. Tieni inoltre presente che i servizi e la rappresentazione complicano CurrentUser, poiché l'account di esecuzione e il caricamento del profilo determinano il contesto di decrittografia.
Da cosa DPAPI non protegge?
DPAPI non aiuta quando il codice viene eseguito con gli stessi privilegi utente (tale codice può sostanzialmente decrittografare tutto ciò che l'utente può decrittografare) né contro una macchina completamente compromessa o un controllo a livello di amministratore, e non protegge il testo in chiaro in memoria dopo la decrittografia. Inoltre, non giustifica segreti di lunga durata che non dovrebbero essere affatto sul client: se ogni installazione condivide la stessa chiave API, l'estrazione da ogni singola macchina si estende a tutto. Il punto di forza di DPAPI è il lato relativo alla perdita di file, allo smarrimento e all'accesso di altri utenti, non al lato della macchina compromessa.
Devo crittografare l'intero file di configurazione con DPAPI?
No: proteggi solo gli oggetti segreti. Il server URLs, i nomi utente, i nomi dei database e i flag delle funzionalità possono in genere rimanere in testo normale, mentre le password, i token API, i token di aggiornamento e le credenziali delle cartelle condivise sono gli obiettivi di protezione. Ciò mantiene la configurazione modificabile e le differenze rivedibili rendendo chiaro ciò che è segreto. Archivia il file in una posizione per utente come %LocalAppData%, non impegnare nemmeno il testo cifrato su Git e non registrare valori decrittografati: il dump di una stringa di connessione in caso di errore trasforma i tuoi log nel magazzino di testo in chiaro.

Profilo dell’autore

Pagina di presentazione dell’autore dell’articolo.

Go Komura

Rappresentante di KomuraSoft LLC

Specializzato nello sviluppo di software Windows, nella consulenza tecnica e nell’analisi dei malfunzionamenti, soprattutto nei progetti con sistemi esistenti e guasti difficili da riprodurre.

Torna al blog