Condividi tramite


Creare programmi C++ affidabili e sicuri

La pubblicazione del Stati Uniti governo NISTIR 8397: Linee guida sugli standard minimi per la verifica dello sviluppatore del software contiene indicazioni eccellenti su come creare software affidabile e sicuro in qualsiasi linguaggio di programmazione.

Questo documento segue la stessa struttura di NISTIR 8397. Ogni sezione:

  • riepiloga come usare i prodotti per sviluppatori Microsoft per C++ e altri linguaggi per soddisfare le esigenze di sicurezza di tale sezione e
  • fornisce indicazioni per ottenere il massimo valore in ogni area.

2.1 Modellazione delle minacce

Riepilogo

La modellazione delle minacce è un processo prezioso, soprattutto se applicato in modo da soddisfare le esigenze di sviluppo e che riduce il rumore.

Raccomandazioni

La modellazione delle minacce deve far parte del ciclo di vita dinamico dello sviluppo della sicurezza (SDL). È consigliabile che per il prodotto nel suo complesso, per una funzionalità specifica o per una modifica importante della progettazione o dell'implementazione:

  • Avere un sdl solido e dinamico che consente il coinvolgimento anticipato con i team di sviluppo e il ridimensionamento dei diritti dell'approccio.
  • Applicare la modellazione delle minacce in modo mirato. Applicare la modellazione delle minacce a tutte le funzionalità, ma iniziare in modo tattico con funzionalità esposte, complesse o critiche. Applicarlo regolarmente invece come parte di una revisione del prodotto dall'alto verso il basso.
  • Applicare la modellazione delle minacce in anticipo (come per tutti i requisiti di sicurezza), quando è ancora possibile modificare la progettazione. Inoltre, i modelli di minaccia fungono da input per altri processi, ad esempio la riduzione della superficie di attacco o la progettazione per la sicurezza. I modelli di minaccia creati in un secondo momento sono al massimo "sondaggi" per test di penna (test di penetrazione) o aree che necessitano di test di sicurezza come la fuzzing. Dopo aver creato un modello di minaccia di base, pianificare di continuare a eseguire l'iterazione quando cambia la superficie di attacco.
  • Usare l'inventario delle risorse e la conformità per tenere traccia in modo appropriato di ciò che costituisce un prodotto e tenere traccia degli artefatti di sicurezza (inclusi i modelli di minaccia) insieme agli asset a cui si applicano. Questo approccio consente una valutazione dei rischi automatizzata migliore e l'attenzione alle attività di sicurezza sui componenti o sulle funzionalità specifiche che cambiano.
  • In Azure, Microsoft Threat Modeling Tool è stato aggiornato nel 2022 per lo sviluppo di Azure. Per altre informazioni, vedere Panoramica di Microsoft Threat Modeling Tool - Azure

Fattori e procedure di supporto

Per applicare correttamente la modellazione delle minacce ed evitare sottoutilizza/uso eccessivo, è stato rilevato che i concetti di base seguenti devono essere affrontati per primi.

Approccio allo sviluppo

Prima di tutto, comprendere l'approccio di sviluppo del team. Per i team con flussi di lavoro di sviluppo agile che inseriscono decine di modifiche alla produzione quotidianamente, non è pratico o ragionevole richiedere un aggiornamento del modello di minaccia per ogni modifica funzionale. Dall'inizio, invece, quando si scrivono i requisiti funzionali di una funzionalità, prendere in considerazione l'inclusione di un questionario sui requisiti di sicurezza. Il questionario deve concentrarsi su domande specifiche sulla funzionalità per determinare quali aspetti futuri si applicano a SDL. Ad esempio:

  • La funzionalità apporta una modifica importante nella progettazione di come viene fornito l'isolamento dei clienti in un ambiente multi-tenant? In tal caso, prendere in considerazione l'esecuzione di un modello di minaccia completo.
  • Una nuova funzionalità consente caricamenti di file? In tal caso, forse ciò che è più appropriato è una valutazione della sicurezza delle applicazioni Web.
  • Si tratta principalmente di una modifica funzionale dell'interfaccia utente? In tal caso, forse nulla è necessario oltre gli strumenti automatizzati tradizionali.

I risultati del questionario sulla sicurezza informano le tecniche SDL da associare a quale unità di sviluppo. Informa anche i partner di sviluppo delle sequenze temporali SDL della funzionalità, in modo che possano collaborare al momento giusto.

Inventario prodotti

In secondo luogo, mantenere un forte inventario delle risorse dei prodotti a cui si è tenuti a valutare. I prodotti stanno crescendo in complessità. È comune scrivere software per i dispositivi connessi che dispongono di:

  • sensori (ad esempio passeggeri e veicoli),
  • reti basate su autobus che comunicano con altri componenti nel veicolo (ad esempio CANBUS o PROFIBUS),
  • wireless/cellulare/Bluetooth per la comunicazione con i dispositivi dei clienti e i back-end cloud,
  • Machine Learning nel cloud che inserisce nuovamente nel dispositivo o in un'applicazione di gestione della flotta,
  • e altro ancora.

In questi prodotti complessi, la modellazione delle minacce è fondamentale. La disponibilità di un inventario asset sicuro consente di visualizzare l'intero stack di prodotti per visualizzare l'immagine completa e di visualizzare le posizioni chiave che devono essere valutate per il modo in cui una funzionalità nuova o modificata influisce sulla sicurezza del prodotto.

Granularità e integrazione

Stabilire sistemi per misurare la conformità usando metriche chiare.

  • Misurare regolarmente la conformità per lo sviluppo a livello di funzionalità. La conformità delle funzionalità in genere deve essere misurata con una maggiore frequenza e una granularità inferiore, talvolta anche nel sistema dello sviluppatore o in fase di commit/unione del codice.
  • Valutare periodicamente la sicurezza per il prodotto più ampio in cui viene utilizzata una funzionalità o un componente. Le valutazioni più ampie vengono in genere eseguite con una frequenza inferiore e una granularità più ampia, ad esempio in fase di test del modulo o del sistema.

Ridimensiona

Mantenere un sistema di inventario degli asset appropriato che acquisisce e mantiene gli artefatti di sicurezza e l'output delle revisioni del modello di minaccia. Avere un inventario chiaro consente di valutare gli output di revisione per i modelli e prendere decisioni intelligenti su come perfezionare regolarmente il programma di sicurezza del prodotto.

Provare a combinare questionari sulla sicurezza in fase di requisiti, risultati della modellazione delle minacce, risultati della valutazione della sicurezza e risultati degli strumenti automatizzati. La combinazione consente di automatizzare un punto di vista del rischio relativo di un determinato prodotto, idealmente come "dashboard", per informare i team di sicurezza su cosa concentrarsi su per ottenere il miglior valore dalla modellazione delle minacce.

2.2 Test automatizzati

Riepilogo

I test automatizzati sono un modo importante per garantire la qualità e la sicurezza del codice. Sono una parte integrante del supporto di altre aree menzionate in questo documento, ad esempio la modellazione delle minacce. Se abbinati ad altre procedure di codifica sicure, consentono di proteggersi da bug e vulnerabilità introdotti nella codebase.

Attributi chiave

I test devono essere affidabili, coerenti e isolati. Questi test devono coprire la maggior parte del codice possibile. Quando possibile, tutte le nuove funzionalità e correzioni di bug devono avere test corrispondenti per garantire la sicurezza e l'affidabilità a lungo termine del codice. Eseguire test automatizzati regolarmente e nel maggior numero possibile di ambienti per assicurarsi che vengano eseguiti e che coprono tutte le aree:

  • Il primo posto in cui devono essere eseguiti è nel computer che sta apportando le modifiche. L'esecuzione di test viene eseguita più facilmente all'interno dell'IDE usato per la modifica o come script nella riga di comando, perché lo sviluppatore apporta le modifiche.
  • La posizione successiva in cui devono essere eseguite è parte del processo di commit/unione della richiesta pull.
  • L'ultima posizione in cui eseguire i test è parte di una pipeline di integrazione continua e distribuzione continua (CI/CD) o nelle build candidate della versione.

L'ambito dei test dovrebbe aumentare a ogni passaggio, con l'ultimo passaggio che fornisce la copertura completa per qualsiasi altro passaggio potrebbe non essere possibile.

Uso e manutenzione continui

L'affidabilità dei test è una parte importante della gestione dell'efficacia del gruppo di test. Gli errori di test devono essere assegnati e esaminati, con potenziali problemi di sicurezza che ottengono priorità elevata e vengono aggiornati entro un intervallo di tempo predeterminato e richiesto. Ignorare gli errori di test non deve essere una pratica comune, ma deve richiedere una motivazione e un'approvazione forti. Gli errori di test causati da problemi all'interno del gruppo di test stesso devono essere trattati come altri errori, per evitare un decadire nella copertura in cui i problemi del prodotto potrebbero non essere superati.

Tipi di test, in particolare unit test

Esistono diversi tipi di test automatizzati e, anche se non tutti sono applicabili a tutte le applicazioni, un gruppo di test valido contiene una selezione di diversi tipi. I test case basati su codice, ad esempio gli unit test, sono i più comuni e più integrali, applicabili a tutte le applicazioni e coprono intenzionalmente il maggior numero possibile di percorsi di codice per la correttezza. Questi test devono essere di piccole dimensioni, rapidi e non influiscono sullo stato del computer, in modo che la suite completa di test possa essere eseguita rapidamente e spesso. Se possibile, eseguire test in molti computer con configurazioni hardware diverse per rilevare problemi che non sono riproducibili in un singolo tipo di computer.

Visual Studio

Esplora test di Visual Studio supporta in modo nativo molti dei framework di test C++ più diffusi e include opzioni per installare le estensioni per più framework. Questa flessibilità è utile per eseguire un subset di test che coprono il codice su cui si sta lavorando e semplifica il debug degli errori di test man mano che si verificano. Visual Studio semplifica anche la configurazione di nuovi gruppi di test per i progetti esistenti e fornisce strumenti utili, ad esempio CodeLens, per semplificare la gestione di questi test. Per altre informazioni sulla scrittura, l'esecuzione e la gestione di test C/C++ con Visual Studio, vedere Scrivere unit test per C/C++ - Visual Studio (Windows).

In Azure e GitHub CI/CD

I test che eseguono la verifica più approfondita e richiedono più tempo per l'esecuzione, ad esempio l'analisi statica, il rilevamento dei componenti e così via, sono buoni candidati per il test delle richieste pull o i test di integrazione continua. Azure DevOps e GitHub Actions semplificano l'esecuzione di convalide automaticamente e bloccano le verifiche del codice in caso di esito negativo della convalida. L'applicazione automatica garantisce che tutto il codice archiviato sia sicuro in base a questi controlli più rigorosi in esecuzione. Azure Pipelines e Azure DevOps Build Validation sono descritti qui:

2.3 Analisi statica o basata sul codice

Per impostazione predefinita, l'analisi statica/binaria di riepilogo deve essere abilitata per impostazione predefinita. L'analisi statica analizza un programma per i criteri di sicurezza e sicurezza necessari al momento della compilazione, non in fase di esecuzione quando un exploit può verificarsi nel computer del cliente. L'analisi statica può analizzare il programma in formato codice sorgente o in formato eseguibile compilato.

Raccomandazioni consigliate da Microsoft:

  • Abilitare l'analisi statica per tutti i programmi C++, sia per il codice sorgente di input (prima della compilazione) che per i file binari eseguibili (dopo la compilazione). "Abilita" può significare eseguire l'analisi durante ogni compilazione nel computer dello sviluppatore o come compilazione separata per esaminare il codice in un secondo momento o come requisito di archiviazione.
  • Incorporare l'analisi statica nelle pipeline ci come forma di test.
  • L'analisi statica per definizione include falsi positivi ed essere preparati a incorporare tale fatto nel ciclo di feedback qualitativo. Abilitare rapidamente tutti gli avvisi di basso falso positivo in anticipo. Quindi essere proattivi per aumentare gradualmente il numero di regole per cui la codebase compila la pulizia degli avvisi man mano che si aggiungono regolarmente altre regole che contrassegnano bug importanti a scapito di falsi positivi gradualmente più elevati (inizialmente, prima che la codebase sia stata pulita anche per tali regole).
  • Usare sempre le versioni supportate più recenti di Visual Studio e configurare l'ambiente di progettazione per usare rapidamente le versioni più recenti delle patch non appena diventano disponibili, senza ritardare la fase/ciclo di sviluppo successivo.

Gli strumenti chiave Sono consapevoli di e usano quanto segue:

Note:

  • /analyze consente l'analisi statica del codice C++ in fase di compilazione per identificare le vulnerabilità critiche del codice di sicurezza e affidabilità. Deve essere abilitato in tutta la sequenza temporale di sviluppo di un programma C++. Per iniziare, abilitare almeno "Microsoft Native Recommended" per impostazione predefinita come baseline minima. Consultare quindi la documentazione per informazioni su come specificare altre regole, in particolare le regole delle linee guida di base di C++, come richiesto dai criteri di progettazione. La funzionalità analisi statica del codice sorgente è disponibile sia nell'IDE di Visual C++ che in Strumenti di compilazione da riga di comando.
  • /W4 e /WX devono essere abilitati laddove possibile, per assicurarsi di compilare il codice in modo pulito a livelli di avviso elevati (W4) e considerare gli avvisi come errori che devono essere corretti (WX). Queste opzioni consentono di trovare errori di dati non inizializzati che altri strumenti di analisi statica non possono controllare, perché gli errori diventano visibili solo dopo che il back-end del compilatore esegue l'analisi interproedurale e l'inlining.
  • L'analisi binaria binSkim garantisce che i progetti consentano un'ampia gamma di funzionalità di sicurezza. BinSkim genera PDB e altri output che semplificano la verifica della catena di custodia e la risposta efficiente ai problemi di sicurezza. Microsoft consiglia di eseguire lo strumento BinSkim per analizzare tutti i file binari eseguibili (.syso .dll .exe) prodotti o utilizzati dai programmi. La Guida per l'utente di BinSkim include un elenco degli standard di sicurezza supportati. Microsoft consiglia di risolvere tutti i problemi segnalati come "errori" dallo strumento BinSkim. I problemi segnalati come "avvisi" devono essere valutati in modo selettivo, perché la risoluzione può avere implicazioni sulle prestazioni o potrebbe non essere necessaria.

In Azure e GitHub CI/CD Microsoft consiglia di abilitare sempre il codice sorgente e l'analisi statica binaria negli scenari CI/CD di rilascio. Eseguire l'analisi dell'origine immediatamente nel computer dello sviluppatore locale o almeno per ogni richiesta di commit o pull per rilevare i bug di origine il prima possibile e ridurre al minimo i costi complessivi. I bug a livello binario tendono a essere introdotti più lentamente, quindi potrebbe essere sufficiente eseguire l'analisi binaria in scenari CI/CD meno frequenti ,ad esempio compilazioni notturne o settimanali.

2.4 Esaminare i segreti hardcoded

Riepilogo

Non impostare i segreti hardcoded all'interno del software. È possibile trovare e rimuovere segreti dal codice sorgente in modo efficiente usando strumenti affidabili che consentono di analizzare l'intera codebase sorgente. Dopo aver trovato i segreti, spostarli in un luogo sicuro seguendo le linee guida per l'archiviazione sicura e l'uso dei segreti.

Problema

"Segreti" significa entità che stabiliscono l'identità e forniscono l'accesso alle risorse o che vengono usate per firmare o crittografare i dati sensibili. Ad esempio, password, chiavi di archiviazione, stringa di connessione e chiavi private. È tentata di mantenere segreti nel prodotto software in modo che possano essere facilmente ottenuti quando necessario dal software. Tuttavia, questi segreti hardcoded possono causare gravi o irreversibili eventi imprevisti di sicurezza man mano che vengono facilmente individuati e possono essere usati per compromettere il servizio e i dati.

Prevenzione

I segreti hardcoded nel codice sorgente (come BLOB crittografato o testo normale) sono una vulnerabilità di sicurezza. Ecco le linee guida generali su come evitare segreti nel codice sorgente:

  • Usare uno strumento di controllo preliminare per analizzare e rilevare potenziali segreti hardcoded nel codice prima dell'invio al controllo del codice sorgente.
  • Non inserire credenziali di testo non crittografate nel codice sorgente o nei file di configurazione.
  • Non archiviare credenziali di testo non crittografate in SharePoint, OneNote, condivisioni file e così via. Oppure condividerli tramite posta elettronica, messaggistica istantanea e così via.
  • Non crittografare un segreto con una chiave di decrittografia facilmente individuabile. Ad esempio, non archiviare un file PFX insieme a un file contenente la password.
  • Non crittografare un segreto con una decrittografia debole. Ad esempio, non crittografare un file PFX con una password debole o comune.
  • Evitare di inserire le credenziali crittografate nel codice sorgente. Usare invece i segnaposto nell'origine e consentire al sistema di distribuzione di sostituirli con i segreti degli archivi approvati.
  • Applicare gli stessi principi ai segreti in ambienti come i test, la gestione temporanea e così via, come avvieni nelle distribuzioni di produzione. Gli avversari spesso usano sistemi non di produzione perché sono meno gestiti, quindi li usano per passare all'ambiente di produzione.
  • Non condividere segreti tra distribuzioni, ad esempio test, gestione temporanea e produzione.

Anche se non direttamente correlati ai segreti hardcoded, tenere presente anche la protezione dei segreti per test, sviluppo e produzione:

  • Ruotare periodicamente i segreti e ogni volta che potrebbero essere stati esposti. Avere dimostrato la capacità di ruotare/ridistribuire i segreti è la prova di un sistema sicuro. In particolare, l'assenza di questa capacità è ancora più forte di una vulnerabilità inevitabile.
  • Non dare a alla logica comune dello sviluppatore che "le mie credenziali di test non creano rischi". In pratica, quasi sempre lo fanno.
  • Prendere in considerazione la possibilità di allontanarsi dai segreti (ad esempio, password, chiavi di connessione) interamente in base al controllo degli accessi in base al ruolo/soluzioni guidate dall'identità come una buona soluzione di progettazione in grado di impedire completamente la gestione errata dei segreti.

Rilevamento

I componenti legacy del prodotto potrebbero contenere segreti hardcoded nascosti nel codice sorgente. A volte i segreti dei computer desktop degli sviluppatori possono entrare nel ramo remoto e unire i segreti in modo involontario nel ramo di rilascio. Per individuare segreti che potrebbero essere nascosti nel codice sorgente, è possibile usare strumenti che consentono di analizzare il codice per individuare segreti hardcoded:

Correzione

Quando le credenziali vengono trovate nel codice sorgente, l'urgenza immediata consiste nell'invalidare la chiave esposta ed eseguire un'analisi dei rischi in base all'esposizione. Anche se il sistema deve rimanere in esecuzione, è possibile abilitare un gestore dei segreti per la correzione attenendosi alla procedura seguente:

  1. Se la correzione consente il passaggio alle identità gestite o richiede l'eliminazione in un gestore dei segreti, ad esempio Azure Key Vault (AKV), eseguire prima di tutto questa operazione. Ridistribuire quindi con l'identità o la chiave aggiornata.
  2. Invalidare il segreto esposto.
  3. Eseguire la valutazione del controllo/rischio dei potenziali danni causati da compromissione.

Per proteggere le chiavi crittografiche e altri segreti usati da app e servizi cloud, usare Azure Key Vault con criteri di accesso appropriati.

Se un'esposizione compromette determinati dati/informazioni personali dei clienti, potrebbe richiedere altri requisiti di conformità/creazione di report.

Rimuovere i segreti ora invalidati dal codice sorgente e sostituirli con metodi alternativi che non espongono i segreti direttamente nel codice sorgente. Cercare le opportunità di eliminare i segreti laddove possibile usando strumenti come Azure AD. È possibile aggiornare i metodi di autenticazione per sfruttare i vantaggi delle identità gestite tramite Azure Active Directory. Usare solo gli archivi approvati per archiviare e gestire segreti come Azure Key Vault (AKV). Per altre informazioni, vedi:

Azure DevOps (AzDO)

Gli utenti di AzDO possono analizzare il codice tramite GitHub Advanced Security for Azure DevOps (GHAzDO). GHAzDO consente inoltre agli utenti di evitare le esposizioni segrete abilitando La protezione push nei repository, rilevando potenziali esposizioni prima che vengano perse. Per altre informazioni su come rilevare segreti hardcoded nel codice in Azure DevOps, vedere Analisi dei segreti per Github Advanced Security per Azure DevOps in ognuno dei collegamenti seguenti:

In GitHub

L'analisi dei segreti è disponibile in GitHub.com in due forme:

  • Avvisi di analisi dei segreti per i partner. Viene eseguito automaticamente in tutti i repository pubblici. Tutte le stringhe che corrispondono ai modelli forniti dai partner di analisi dei segreti vengono segnalate direttamente al partner pertinente.
  • Avvisi di analisi dei segreti per gli utenti. È possibile abilitare e configurare un'analisi aggiuntiva per i repository di proprietà delle organizzazioni che usano GitHub Enterprise Cloud e avere una licenza per GitHub Advanced Security. Questi strumenti supportano anche repository privati e interni.

GitHub fornisce modelli noti di segreti per partner e utenti che possono essere configurati per soddisfare le proprie esigenze. Per altre informazioni, vedere:

Nota

GitHub Advanced Security per Azure DevOps offre la stessa analisi dei segreti, l'analisi delle dipendenze e le soluzioni di analisi del codice CodeQL già disponibili per gli utenti di GitHub e le integra in modo nativo in Azure DevOps per proteggere Azure Repos e Pipeline.

Risorse aggiuntive

2.5 Eseguire con controlli e protezione forniti dal sistema operativo e dalla lingua

Riepilogo

La protezione avanzata binaria viene eseguita applicando controlli di sicurezza in fase di compilazione. Tra queste sono incluse le mitigazioni che:

  • prevenire vulnerabilità sfruttabili nel codice,
  • abilitare i rilevamenti di runtime che attivano difese di sicurezza sullo sfruttamento e
  • abilitare la produzione e l'archiviazione dei dati per limitare i danni causati da eventi imprevisti di sicurezza.

I consumer binari devono acconsentire esplicitamente alle funzionalità di sicurezza di Windows per ottenere il massimo vantaggio della protezione avanzata.

Microsoft offre un set di strutture specifiche dei progetti C++ per aiutare gli sviluppatori a scrivere e distribuire codice più sicuro e sicuro. Gli sviluppatori C++ devono inoltre rispettare gli standard di sicurezza comuni ai linguaggi che generano codice eseguibile. Microsoft gestisce BinSkim, un controllo binario OSS pubblico che consente di applicare l'uso di molte protezioni descritte in questa sezione. Per altre informazioni su BinSkim, vedere Guida dell'utente di Binskim | GitHub

I controlli a livello binario variano in base alla posizione in cui vengono applicati nel processo di progettazione. È necessario distinguere tra le opzioni del compilatore e del linker che: sono rigorosamente in fase di compilazione, modificano la generazione del codice con sovraccarico di runtime e modificano la generazione del codice per ottenere la compatibilità con le protezioni del sistema operativo.

Le impostazioni dello sviluppatore devono preferire l'abilitazione del maggior numero possibile di analisi statiche, consentire la produzione di dati privati per accelerare il debug e così via. Le build di rilascio devono essere ottimizzate in base a una combinazione appropriata di problemi di sicurezza, prestazioni e altre problematiche di generazione del codice. I processi di rilascio devono essere configurati per generare e gestire correttamente i dati di compilazione pubblici e privati ,ad esempio i simboli pubblici e privati.

Rimanere aggiornati: usare sempre compilatori e strumenti aggiornati

Compilare tutto il codice con i set di strumenti correnti per trarre vantaggio dal supporto del linguaggio aggiornato, dall'analisi statica, dalla generazione del codice e dai controlli di sicurezza. Poiché i compilatori influiscono su ogni componente generato, il potenziale di regressione sull'aggiornamento degli strumenti è relativamente elevato. L'uso di compilatori obsoleti crea un rischio particolare per l'azione correttiva durante la risposta a un evento imprevisto di sicurezza, perché i team potrebbero non avere tempo sufficiente per aggiornare i compilatori. Microsoft consiglia ai team di sviluppare la funzionalità per aggiornare e testare regolarmente gli aggiornamenti del compilatore.

Usare metodi di sviluppo sicuri, versioni del linguaggio, framework/API

Il codice deve usare metodologie di sviluppo, versioni del linguaggio, framework, API e così via, che riducono al minimo i rischi promuovendo sicurezza e semplicità in C++, tra cui:

Utilizzare dipendenze sicure

I file binari non devono essere collegati a librerie e dipendenze non sicure. I team di sviluppo devono tenere traccia di tutte le dipendenze esterne e risolvere le vulnerabilità di sicurezza identificate o cve in questi componenti aggiornando le versioni più sicure quando sono soggette a tali vulnerabilità.

Ottimizzare le garanzie di provenienza del codice e l'efficienza della risposta alla sicurezza

La compilazione deve abilitare garanzie avanzate di provenienza del codice per rilevare e impedire l'introduzione di backdoor e altro codice dannoso. I dati risultanti, fondamentali anche per il debug e l'analisi, devono essere archiviati per tutte le versioni software per ottenere una risposta di sicurezza efficiente se vengono compromessi. Le opzioni del compilatore seguenti generano informazioni critiche per una risposta di sicurezza:

  • /ZH:SHA_SHA256 in Visual C++ - Assicura che venga usato un algoritmo di crittografia sicuro per generare tutti gli hash dei file di origine PDB.
  • /Zi, /ZI (Formato informazioni di debug) in Visual C++ - Oltre alla pubblicazione di simboli rimossi per la raccolta di dati di arresto anomalo del sistema e altri scenari d'uso pubblici, assicurarsi che le compilazioni producano e archiviino PDF privati per tutti i file binari rilasciati. Gli strumenti di analisi binaria richiedono simboli completi per verificare se molte mitigazioni della sicurezza sono state abilitate in fase di compilazione. I simboli privati sono fondamentali nella risposta alla sicurezza e ridurre i costi di debug e indagine quando i tecnici stanno correndo per valutare e limitare i danni quando si verifica un exploit.
  • /SOURCELINK in Visual C++ Linker - Includi file Sourcelink in PDB: il collegamento all'origine è un sistema indipendente dal linguaggio e dal controllo del codice sorgente che fornisce il debug dell'origine per i file binari. Il debug dell'origine aumenta notevolmente l'efficienza della gamma di convalide di sicurezza non definitive e la risposta agli eventi imprevisti post-rilascio.

Abilitare gli errori del compilatore per evitare problemi in fase di creazione del codice

La compilazione deve abilitare i controlli del compilatore pertinenti per la sicurezza come errori di interruzione, ad esempio:

Contrassegnare i file binari come compatibili con le mitigazioni della sicurezza del runtime del sistema operativo

Le impostazioni del compilatore e del linker devono acconsentire esplicitamente alle funzionalità di generazione del codice che rilevano e attenuano l'esecuzione di codice dannoso, tra cui:

Impedire la divulgazione di informazioni riservate

Le impostazioni del compilatore devono acconsentire esplicitamente alla prevenzione dell'individuazione delle informazioni riservate. Negli ultimi anni, i ricercatori hanno scoperto perdite di informazioni impreviste che hanno origine con caratteristiche hardware come l'esecuzione speculativa.

A livello di software, i dati riservati potrebbero essere trasmessi agli utenti malintenzionati in caso di perdita imprevista. L'impossibilità di inizializzare i buffer e altri usi impropri del buffer potrebbe causare la perdita di dati riservati privati a utenti malintenzionati che chiamano l'API attendibile. Questa classe di problemi viene gestita in modo ottimale abilitando l'analisi statica aggiuntiva e usando contenitori di risorse sicure, come descritto in precedenza.

  • /Qspectre - Attenuare gli attacchi di canale laterale dell'esecuzione speculativa: inserisce istruzioni sulle barriere che consentono di impedire la divulgazione di dati sensibili generati dall'esecuzione speculativa. Queste mitigazioni devono essere abilitate per il codice che archivia i dati sensibili in memoria e opera attraverso un limite di attendibilità. Microsoft consiglia sempre di misurare l'impatto delle prestazioni rispetto ai benchmark appropriati quando si abilitano le mitigazioni Spectre a causa della possibilità di introdurre controlli di runtime in blocchi o cicli critici per le prestazioni. Questi percorsi di codice possono disabilitare le mitigazioni tramite il spectre(nomitigation) declspec modificatore. Anche i progetti che abilitano "/Qspectre" devono essere collegati a librerie compilate con queste mitigazioni, incluse le librerie di runtime Microsoft.

2.6 Test case scatola nera

Riepilogo

I test black box non si basano sulla conoscenza dei lavori interni del componente testato. I test black box sono progettati per testare la funzionalità end-to-end delle funzionalità del prodotto a qualsiasi livello o livello. I test black box possono essere test funzionali, test dell'interfaccia utente, test delle prestazioni e test di integrazione. I test black box sono utili per misurare l'affidabilità generale e la correttezza funzionale e garantire che il prodotto si comporti come previsto.

Relazione con altre sezioni

Questi tipi di test basati sui requisiti sono utili per convalidare i presupposti effettuati nel modello di minaccia e per coprire le potenziali minacce, come illustrato in questa sezione. Questi test sono utili per testare l'integrazione tra componenti separati del prodotto, in particolare quelli che si trovano oltre i limiti di attendibilità, come descritto nel modello di minaccia. I test case black box sono utili anche per testare tutti i tipi di case perimetrali per la convalida dell'input dell'utente. I test dei casi perimetrali noti e dei casi di errore sono entrambi utili. La fuzzing è utile anche per testare casi meno evidenti.

Automazione e regressione

Eseguire regolarmente questi test e confrontare i risultati con le esecuzioni precedenti per rilevare modifiche di rilievo o regressioni delle prestazioni. Inoltre, l'esecuzione di questi test in molti computer e configurazioni di installazione diverse può aiutare a risolvere eventuali problemi che possono verificarsi da architetture diverse o modifiche all'installazione.

Dump di arresto anomalo del sistema

Questi test consentono di trovare problemi con l'affidabilità, di poter testare molti scenari diversi che potrebbero verificarsi arresti anomali, blocchi, deadlock e così via. Raccogliendo dump di arresto anomalo come parte degli errori di test, è possibile importare i dump direttamente in Visual Studio per analizzare ulteriormente quali parti del codice stanno riscontrando questi problemi. Se si eseguono test funzionali da Visual Studio, è possibile replicare ed eseguire facilmente il debug degli errori visualizzando esattamente dove la casella nera il test ha esito negativo ed è possibile testare rapidamente le correzioni.

Per iniziare a eseguire il debug dei test, vedere Eseguire il debug di unit test con Esplora test - Visual Studio (Windows)

In Azure

Azure DevOps consente anche di gestire e convalidare questi test con l'uso dei piani di test. Questi test possono essere usati per garantire l'approvazione con la convalida manuale e per eseguire test automatizzati associati ai requisiti del prodotto. Altre informazioni sui piani di test di Azure e sull'uso per eseguire test automatizzati sono disponibili qui:

2.7 Test case basati su codice

Riepilogo

I test case basati su codice sono parte integrante della gestione della sicurezza e dell'affidabilità del prodotto. Questi test devono essere piccoli e veloci e non dovrebbero avere un impatto l'uno sull'altro in modo che possano essere eseguiti in parallelo. I test basati su codice sono facili per gli sviluppatori di eseguire localmente nel computer di sviluppo ogni volta che apportano modifiche al codice senza doversi preoccupare di rallentare il ciclo di sviluppo.

Tipi e relazioni con altre sezioni

I tipi comuni di test case basati su codice includono:

  • unit test,
  • test con parametri per coprire le funzioni con più tipi di input,
  • test dei componenti per mantenere separati ogni componente di test e
  • test fittizi per convalidare parti del codice che comunicano con altri servizi, senza espandere l'ambito del test per includere tali servizi stessi.

Questi test sono basati sul codice interno scritto, mentre i test black box si basano sui requisiti funzionali esterni del prodotto.

Obiettivo

Tramite questi test, l'obiettivo è raggiungere un livello elevato di copertura dei test sul codice. È consigliabile tenere traccia attivamente di questa copertura e dove esistono lacune. Man mano che si aggiungono altri test che esercitano più percorsi di codice, aumenta l'attendibilità complessiva della sicurezza e dell'affidabilità del codice.

Visual Studio

Gli strumenti di Esplora test in Visual Studio semplificano l'esecuzione di questi test di frequente e ricevono commenti e suggerimenti sulla velocità di superamento/esito negativo e sui percorsi degli errori. Molti dei framework di test supportano anche le funzionalità CodeLens per visualizzare lo stato dei test nella posizione del test stesso, rendendo più semplice l'aggiunta e la gestione della suite di test. Esplora test semplifica la gestione di questi test, consentendo gruppi di test, playlist di test personalizzate, filtro, ordinamento, ricerca e altro ancora.

Per altre informazioni, vedi:

Visual Studio include anche strumenti per tenere traccia del code coverage. Questi strumenti consentono di assicurarsi che le modifiche al codice apportate siano coperte da test esistenti o per aggiungere nuovi test per coprire percorsi di codice nuovi e non verificati. Gli strumenti mostrano anche la percentuale di code coverage per assicurarsi che venga mantenuta al di sopra di un livello di destinazione per garantire la sicurezza nella qualità complessiva del codice.

Per informazioni su questi strumenti, vedere Test di code coverage - Visual Studio (Windows)

In Azure

Azure DevOps consente anche di tenere traccia dei risultati del code coverage per l'intero prodotto come parte del processo della pipeline di compilazione. Per altre informazioni, vedere Esaminare il code coverage - Azure Pipelines.

2.8 Test case cronologici

Riepilogo

I test case cronologici, noti anche come test case di regressione, impediscono che i vecchi problemi si ripetano e aumentino la copertura complessiva dei test del prodotto. È necessario assicurarsi che quando viene corretto un bug, il progetto aggiunge anche un test case corrispondente. Nel corso del tempo, man mano che vengono apportate correzioni, l'affidabilità complessiva del gruppo di test continuerà a migliorare, offrendo migliori garanzie di affidabilità e sicurezza.

Qualità chiave e relazione con altre sezioni

Poiché testano le regressioni di bug, questi test devono essere veloci e facili da eseguire, in modo che possano essere eseguiti insieme ai [test case basati su codice] e contribuire alla code coverage generale del prodotto. Oltre a questo, l'uso di esempi reali dei clienti per ispirare nuovi test case è un ottimo modo per migliorare la copertura e la qualità dei test.

Visual Studio

Visual Studio consente di aggiungere facilmente test alla famiglia di prodotti durante l'esecuzione delle modifiche per correggere il bug e di eseguire rapidamente i test e il code coverage per assicurarsi che tutti i nuovi casi vengano presi in considerazione. Fare riferimento all'ID bug dal sistema di rilevamento dei problemi nel codice in cui si scrive il test è un buon modo per connettere i test di regressione ai problemi corrispondenti. Preferisce usare Azure Boards e i piani di test insieme a Visual Studio:

  • associare test, test case e problemi; e
  • per tenere traccia di tutti gli aspetti di un problema e dei relativi test.

Per altre informazioni, vedi:

Infine, l'integrazione di questi test nell'area di unit test che dovrebbe coprire la sezione del codice consente di mantenere il gruppo di test organizzato e più facile da gestire. È possibile usare il raggruppamento di test di Esplora test per tenere traccia in modo efficace dei test che appartengono insieme. Per altre informazioni, vedere Eseguire unit test con Esplora test - Visual Studio (Windows)

2.9 Fuzzing

Summary Fuzzing (noto anche come test fuzz) è una tecnica di test software automatizzata che implica la fornitura di dati non validi, imprevisti o casuali come input a un programma. Il programma viene quindi monitorato per individuare eccezioni quali arresti anomali, errori predefiniti o inseriti dal compilatore e potenziali perdite di memoria.

Indicazioni

Usare la fuzzing in tutto il software che potrebbe elaborare input non attendibili che un utente malintenzionato potrebbe controllare. Se si sta creando una nuova applicazione e il gruppo di test associato, includere il fuzzing per i moduli chiave il prima possibile. L'esecuzione di fuzzing per la prima volta su un componente software quasi sempre rileva le vulnerabilità effettive che in precedenza erano sconosciute. Una volta che si inizia a fuzzing, non smettere mai.

Relazione con altre sezioni

Quando il fuzzing segnala un errore, fornisce sempre un test case riproducibile che dimostra il bug. Questo test case può essere riprodotto, risolto e quindi aggiunto ai test case cronologici.

Quando si usano entrambi i sanificatori, ad esempio Address Sanitizer (ASan) e fuzzing:

  • Eseguire prima di tutto i test normali con sanificatori abilitati per verificare se sono presenti problemi, quindi una volta che il codice è pulito e pulito, avviare la fuzzing.
  • Per C o C++, sono disponibili compilatori che automatizzano l'inserimento di asserzioni di runtime e metadati che abilitano ASan. Quando viene compilato per ASan, i file binari risultanti si collegano a una libreria di runtime in grado di diagnosticare con precisione 15 categorie di errori di sicurezza della memoria con zero falsi positivi. Per C o C++ quando si dispone di un'origine, usare LibFuzzer, che richiede che ASan sia abilitato per primo.
  • Per le librerie scritte in Java, C#, Python, Rust e così via, usare il framework AFL++.

Qualità chiave

  • La ricerca fuzzing rileva le vulnerabilità spesso perse dall'analisi statica del programma, dai test completi delle funzionalità e dall'ispezione manuale del codice.
  • Fuzzing è un modo efficace per trovare bug di sicurezza e affidabilità nel software, tanto che il ciclo di vita di sviluppo della sicurezza Microsoft richiede la fuzzing in ogni interfaccia non attendibile di ogni prodotto (vedere anche Modellazione delle minacce).
  • Usare sempre la fuzzing per il software che potrebbe elaborare input non attendibili.
  • Il fuzzing è efficace per le applicazioni autonome con parser di dati di grandi dimensioni.

CI/CD di Azure e GitHub

Modificare le build per supportare la creazione continua di file eseguibili che usano LibFuzzer o AFL++. È possibile aggiungere risorse di calcolo aggiuntive necessarie per il fuzzing in servizi come OSS-Fuzz o OneFuzz.

2.10 Analisi delle applicazioni Web

Riepilogo

Nell'ambito di Microsoft Visual C++ in Windows, Microsoft consiglia di:

  • Preferisce TypeScript, JavaScript e ASP.NET per le applicazioni Web.
  • Non scrivere estensioni Web in C++. Microsoft ha deprecato ActiveX.
  • Quando il codice viene compilato in Emscripten/WASM, non è più C++ e altri strumenti si applicano.
  • Microsoft fornisce RESTler, un fuzzer dell'API REST con stato.

Panoramica e qualità chiave

Uno scanner di applicazioni Web esplora un'applicazione Web eseguendo una ricerca per indicizzazione nelle pagine Web ed esaminandola per individuare le vulnerabilità di sicurezza. Questa ricerca per indicizzazione comporta la generazione automatica di input dannosi e la valutazione delle risposte dell'applicazione. L'analisi critica delle applicazioni Web deve coprire/supportare:

  • Cataloga tutte le app Web nella rete, incluse quelle nuove e sconosciute, e viene ridimensionata da poche app a migliaia.
  • Analisi approfondita delle versioni software, dei servizi API SOAP e REST e delle API usate dai dispositivi mobili.
  • Inserimento di primitive di sicurezza nello sviluppo e nella distribuzione di applicazioni in ambienti DevOps. Queste primitive funzionano con il crawler.
  • Rilevamento di malware.

2.11 Controllare i componenti software inclusi

Riepilogo

Gestire il codice C++ come codice scritto in altri linguaggi di programmazione e applicare qualsiasi strumento di analisi della composizione software (SCA) e analisi dell'origine (OA) adottato dall'azienda al codice C++. I flussi di lavoro e l'analisi della sicurezza devono essere progettati come parte dei sistemi CI/CD (integrazione continua e recapito continuo).

Difesa upstream

Per ridurre il rischio di attacchi alle dipendenze upstream, è consigliabile archiviare origini/componenti di terze parti in un asset controllato dall'organizzazione, in cui vengono eseguiti gli strumenti SCA e OA.

  • Gli strumenti devono analizzare e avvisare quando vengono identificate vulnerabilità (inclusi i database pubblici), ad esempio: Home | CVE
  • Eseguire l'analisi statica su tutti i componenti software inclusi nell'applicazione/repository per identificare i modelli di codice vulnerabili.

Difesa delle dipendenze

Eseguire e gestire un controllo delle dipendenze per verificare che tutte queste occorrenze siano tenute in considerazione e coperte dagli strumenti SCA e OA.

  • I componenti devono essere controllati e aggiornati regolarmente alle versioni verificate più recenti.
  • Dipendenze del feed di pacchetti.
  • Gli strumenti SCA/OA coprono e controllano tutte le dipendenze dei pacchetti provenienti da un singolo feed.

SBOM

Produrre un SBOM (fattura software di materiali) con il prodotto che elenca tutte le dipendenze, ad esempio:

  • origin (ad esempio, URL (Uniform Resource Locator))
  • versione
  • coerenza (ad esempio, hash di origine SHA-256) e altri mezzi per convalidare la coerenza, ad esempio compilazioni deterministiche.
  • Richiedere e controllare i file SBOM nelle dipendenze software o prodotti come parte di una compilazione, incluso oss (software open source).
  • Microsoft sta standardizzando e consiglia SPDX (Software Package Data Exchange) versione 2.2 o successiva | Linux Foundation come formato del documento SBOM.
  • Il determinismo di compilazione può essere usato per produrre in modo indipendente file binari identici bit-wise e fornire verifiche indipendenti dell'integrità:
    • Attestazione di riproducibilità di terze parti o di terze parti
    • Altre tecniche, ad esempio la firma binaria tramite un'origine certificato attendibile, possono fornire anche alcune garanzie di integrità binaria.

Risorse aggiuntive

Le soluzioni Microsoft includono le linee guida e i prodotti seguenti: