Secure Coding

Indice dei contenuti

    Il Secure Coding (programmazione sicura) è la disciplina che studia come scrivere software privo delle vulnerabilità che gli attaccanti sfruttano per compromettere sistemi, rubare dati o eseguire codice arbitrario. Non si tratta di aggiungere sicurezza dopo aver scritto il codice, ma di incorporarla nelle scelte di progettazione, nelle API utilizzate e nelle convenzioni di sviluppo fin dall’inizio — secondo il principio Security by Design.

    Il secure coding è il fondamento operativo di un ciclo di sviluppo sicuro (Secure SDLC) e copre tutte le fasi: scelta del linguaggio, gestione della memoria, validazione dell’input, gestione degli errori e delle credenziali.


    Principi cardine

    1. Minimo privilegio

    Il codice deve operare con i privilegi minimi necessari alla sua funzione. Un server web non deve girare come root; un plugin non deve avere accesso al filesystem completo; una query al database deve usare un utente con i soli permessi di lettura se non deve scrivere.

    2. Difesa in profondità

    Nessun singolo controllo di sicurezza è infallibile. Stratificare più meccanismi di difesa (validazione dell’input, encoding dell’output, prepared statement, firewall applicativo) garantisce che il fallimento di uno strato non comprometta l’intero sistema.

    3. Fallimento sicuro (Fail Secure)

    In caso di errore, il sistema deve passare a uno stato sicuro, non a uno stato aperto. Se il controllo di autenticazione fallisce per un errore interno, l’accesso deve essere negato, non concesso.

    4. Validazione di tutto l’input

    Qualsiasi dato proveniente dall’esterno del confine di fiducia (utente, database, API esterna, filesystem, variabili d’ambiente) deve essere considerato non fidato fino a prova contraria. La validazione deve essere:

    • Completa: verificare tipo, lunghezza, formato e range.
    • Lato server: la validazione lato client è bypassabile.
    • Whelist-based: preferire la lista di ciò che è consentito a quella di ciò che è vietato.

    5. Separazione dei privilegi e dei ruoli

    Componenti con funzioni diverse devono avere privilegi separati. Il modulo che autentica l’utente non deve avere accesso diretto al database degli ordini; il frontend non deve conoscere le chiavi di crittografia del backend.

    6. Economia del meccanismo (Keep it Simple)

    La complessità è il nemico della sicurezza. Sistemi semplici hanno meno superficie d’attacco, sono più facili da verificare e da testare. Evitare la re-implementazione di primitive crittografiche, protocolli o meccanismi di autenticazione già consolidati.


    Pratiche concrete per categoria

    Gestione della memoria (C/C++)

    PraticaMotivazione
    Usare strncpy, snprintf al posto di strcpy, sprintfPrevenire buffer overflow
    Verificare sempre i ritorni di mallocEvitare dereferenziazione di NULL
    Non usare mai la stessa area di memoria dopo freePrevenire use-after-free
    Preferire linguaggi memory-safe (Rust, Go) quando possibileEliminare intere classi di vulnerabilità

    Input e output

    PraticaMotivazione
    Prepared statement / parametrizzazione delle queryPrevenire SQL Injection
    Encoding contestuale dell’output (HTML, JavaScript, URL)Prevenire XSS
    Usare sempre printf("%s", input) non printf(input)Prevenire format string attack
    Validare path e canonicalizzare prima dell’accesso al filesystemPrevenire path traversal

    Gestione delle credenziali

    PraticaMotivazione
    Non inserire credenziali nel codice sorgentePrevent secret leakage via repository
    Usare variabili d’ambiente o vault (HashiCorp, AWS Secrets Manager)Separare codice da configurazione sensibile
    Hashare le password con bcrypt/Argon2, non MD5/SHA-1Resistenza a password cracking
    Invalidare token/sessioni dopo logout e scadenzaPrevenire session hijacking

    Crittografia

    PraticaMotivazione
    Non inventare schemi crittografici customLe primitive esistenti sono state analizzate; le custom quasi sempre no
    Usare TLS ≥ 1.2 (preferibilmente 1.3) per tutti i canaliPrevenire intercettazioni
    Generare nonce e IV da CSPRNG, mai da rand()rand() è predicibile; invalida la cifratura
    Preferire cifratura autenticata (AES-GCM, ChaCha20-Poly1305)Rilevare alterazioni del ciphertext

    Standard e linee guida di riferimento

    • CERT Secure Coding Standards (Carnegie Mellon): regole specifiche per C, C++, Java, Perl.
    • OWASP Secure Coding Practices: checklist generica per applicazioni web.
    • CWE (Common Weakness Enumeration): catalogo standardizzato delle debolezze del software, usato per classificare vulnerabilità.
    • SEI CERT C Coding Standard: il riferimento più completo per C embedded e di sistema.
    • MISRA C/C++: standard per codice safety-critical (automotive, medicale, aerospaziale).

    Strumenti a supporto

    CategoriaStrumenti
    Static Analysis (SAST)Semgrep, CodeQL, Coverity, Clang Static Analyzer, Bandit (Python)
    Dynamic Analysis (DAST)AddressSanitizer, Valgrind, OWASP ZAP, Burp Suite
    Dependency scanningDependabot, Snyk, OWASP Dependency-Check
    Secret scanningTruffleHog, GitLeaks, detect-secrets
    FuzzingAFL++, libFuzzer, OSS-Fuzz

    Il secure coding non è una lista di regole da memorizzare, ma un cambio di prospettiva: ogni riga di codice che accetta input dall’esterno è un potenziale confine di fiducia, e ogni confine di fiducia deve essere presidiato esplicitamente.

    Ultimo aggiornamento: