Una roadmap per la memory safety è un piano con responsabili e scadenze, e questo trimestre Lei può scriverne una senza toccare la maggior parte del codice C e C++. La riscrittura viene dopo e riguarda solo le parti che la giustificano. Ciò che serve ora è un quadro chiaro di dove si trova il codice non sicuro, un modo per contenerlo e una traccia scritta di ciò che è stato deciso.
Il documento CISA The Case for Memory Safe Roadmaps descrive come i produttori di software possano passare ai linguaggi memory safe (MSL) per eliminare le vulnerabilità di memory safety. Chiede ai produttori di pubblicare roadmap, in modo che i clienti vedano come si assumono la responsabilità dei risultati di sicurezza, pratichino la trasparenza radicale e adottino un approccio top-down. Sono i principi della Secure by Design, e il documento si rivolge tanto ai dirigenti quanto agli ingegneri. Questo secondo pubblico conta: una roadmap senza uno sponsor resta chiusa in una wiki.
Che cosa dicono le fonti e che cosa no
La pagina di sintesi della CISA cita soltanto la transizione verso gli MSL e la creazione e pubblicazione di una roadmap. Non mostra il contenuto completo, quindi non affermiamo nulla su ciò che il documento dice a proposito di fuzzing o isolamento dei processi. Questi due passaggi, più sotto, sono la nostra prassi per gli anni che separano il "oggi abbiamo C++" dal "abbiamo un linguaggio più sicuro nelle parti che contano". Vanno considerati controlli provvisori, non un sostituto della roadmap.
Le fonti non forniscono nemmeno cifre su quanto rischio elimini ciascun passaggio. Non le inventeremo. Le misuri Lei stesso, a partire dalla storia dei crash e delle vulnerabilità della Sua organizzazione.
Passo 1: fare l'inventario del codice non sicuro
Non si può dare priorità a ciò che non si è elencato. Costruisca una tabella per componente, non per repository, perché un singolo repository spesso mescola un'utility che si può lasciare com'è con un parser di rete.
| Campo | Perché conta |
|---|---|
| Linguaggio e standard | Mostra che cosa si può migrare e che cosa no |
| Raggiungibile dall'esterno? | Rete, file, USB, seriale, canale di aggiornamento |
| Elabora input non attendibile? | Determina la priorità di fuzzing e riscrittura |
| Con quali privilegi viene eseguito? | Determina quanto aiuti l'isolamento |
| Di terze parti o nostro? | Determina chi può correggerlo |
| Responsabile e ultima modifica sostanziale | Il codice morto spesso si può rimuovere |
Includa le librerie vendored e i componenti firmware. I team hardware, in particolare, hanno spesso vecchio codice C in bootloader, driver e gestori degli aggiornamenti che nessuno apre da anni. Anche questi componenti meritano una riga.
Passo 2: isolare ciò che non si può ancora sostituire
Per ogni componente segnato come raggiungibile e che elabora input non attendibile, si chieda che cosa accade se viene compromesso per intero. Se la risposta è "tutto", quella è la Sua prima modifica di progetto. Sposti il parser in un processo separato, ne riduca i privilegi e gli tolga l'accesso a file, socket e chiavi che non gli servono.
Questo non corregge il bug. Limita ciò che il bug vale per un attaccante. È coerente con l'obiettivo indicato nel Secure Software Development Framework (SP 800-218) del NIST: mitigare il potenziale impatto dello sfruttamento di vulnerabilità non ancora scoperte.
Passo 3: fare fuzzing dei punti di ingresso
Prenda dall'inventario i componenti che elaborano input non attendibile e scriva un fuzz harness per ciascuno. Esegua gli harness nella CI con un budget di tempo e conservi il corpus dei crash. Cominci da formati di file, messaggi di protocollo e pacchetti di aggiornamento, perché sono i percorsi che un esterno può raggiungere.
Compili i binari di fuzzing con il rilevamento degli errori di memoria del compilatore attivo, così che un overread silenzioso diventi un errore visibile. Ogni crash analizzato Le fornisce qualcosa di cui la roadmap ha bisogno: evidenza di quali componenti meritano una riscrittura. Se un harness non trova nulla dopo esecuzioni prolungate, anche questo è utile e permette di collocare quel componente più in basso nella graduatoria.
Passo 4: scegliere un breve elenco di riscritture
Usi i primi tre passi per individuare i candidati. Una buona prima riscrittura ha queste caratteristiche:
- Elabora input non attendibile.
- Ha un'interfaccia stretta, quindi si può sostituire dietro la stessa API.
- Ha prodotto crash durante il fuzzing o ha una storia di bug.
- È abbastanza piccola da poter essere completata da un solo team in uno o due cicli di rilascio.
Lasci in C o C++ il codice stabile, interno e non esposto, e lo dichiari nella roadmap. Scrivere "non pianificato, isolato, sottoposto a fuzzing" accanto a un componente è onesto e i clienti sanno come interpretarlo. Questa è la nostra interpretazione della trasparenza radicale: si dichiara la propria posizione, e non è necessario che ogni riga si concluda con una riscrittura. La sintesi della CISA nomina il principio ma non spiega che cosa significhi per i componenti che restano in C o C++.
I nuovi componenti sono il punto di partenza più economico. Decida fin d'ora quale linguaggio useranno i nuovi componenti esposti in rete e lo inserisca negli standard di ingegneria.
Passo 5: metterlo per iscritto e collegarlo al processo
Il NIST descrive l'SSDF come un insieme essenziale di pratiche di alto livello da integrare nel ciclo di vita dello sviluppo che già si adotta, con l'obiettivo di affrontare le cause profonde in modo che le vulnerabilità non si ripresentino. Inserisca le Sue regole di memory safety in quel processo esistente: scelta del linguaggio per il nuovo codice, fuzzing nella CI, un controllo di revisione per i costrutti non sicuri. L'SSDF offre inoltre ad acquirenti e fornitori un vocabolario comune, utile quando il questionario di sicurezza di un cliente chiede della memory safety.
Per le checklist di codifica, il progetto OWASP Secure Coding Practices Quick Reference Guide è archiviato. Le sue checklist sono confluite nella OWASP Developer Guide: usi quella versione anziché un PDF obsoleto.
Da dove cominciare
- Esporti un elenco di tutti i componenti C e C++, inclusi il codice vendored e il firmware, e compili la tabella di inventario qui sopra. Si dia un limite di due settimane.
- Segni i componenti che leggono input non attendibile ed elenchi a che cosa può accedere ciascuno se compromesso.
- Sposti i tre componenti esposti più critici in processi separati a bassi privilegi, oppure pianifichi la modifica con un responsabile indicato per nome.
- Scriva fuzz harness per gli stessi componenti ed eseguali nella CI. Riveda i crash ogni settimana.
- Scelga un componente da riscrivere in un linguaggio memory safe e stabilisca una regola sul linguaggio per tutto il nuovo codice esposto in rete.
- Pubblichi una roadmap interna di una pagina con responsabili e scadenze, poi decida che cosa condividere con i clienti. Se desidera una verifica esterna, il nostro servizio di sviluppo software sicuro e il penetration testing possono rivedere l'inventario e testare i componenti isolati.
