twitterlinkedinmail

Secure Coding: Le principali minacce e contromisure

Indice argomenti:

  • Misplaced Trust
  • Injection Attacks
  • Leaking Sensitive Data
  • Leaking Capabilities
  • Denial of Service
  • Serialization
  • Concurrency, Visibility, and Memory
  • Principle of Least Privilege
  • Security Managers
  • Class Loaders

Scarica le slides

01_le-principali-minacce
Scarica le slides

Sottoscrivi la Newsletter
per scaricare le slides:

Perchè osservare le Secure Coding Guidelines

  • per rispettare obblighi contrattuali;

  • prevenire danni reputazionali;

  • obblighi normativi, ad es GDPR:

    • privacy by design;
    • privacy by default;
    • accountability;

Java è una piattaforma sicura (?)

  • no puntatori e garbage collector;

  • check automatico dei memory boundaries;

  • built-in bytecode verification;

  • JVM security manager;

Tutto questo è sufficiente di per sè per lo sviluppo di codice sicuro?

Misplaced Trust

I software sono oggetti complessi costituiti da componenti che agiscono come sottosistemi;

è necessario pertanto definire correttamente i trust boundaries alla luce di una policy che garantisca la sicurezza complessiva del sistema nel suo complesso;

le API delle librerie di codice di terze parti possono essere considerate un esempio di trust boundary;

Passaggio dei dati tra sottosistemi

I dati di input provenienti da una fonte esterna rispetto al trust boundary devono sempre essere considerati non trustworthy ovvero intrinsecamente insicuri; tali dati devono pertanto essere oggetto di:

  • validazione: verifica del tipo, del range, del dominio di validità;

  • sanificazione: assicurare che i dati si conformino ai requisiti richiesti dal sottosistema;

  • normalizzazione: conversione del dato senza perdita di informazioni al suo formato essenziale, da effettuarsi sull’input nella sua interezza;

Injection Attacks

Un attaccante può sfruttare il passaggio di dati tra sottosistemi, iniettando dati malformati (non sanificati/normalizzati) al fine di eseguire comandi o azioni non autorizzati;

particolare attenzione andrà posta verso i sottosistemi che eseguono:

  • comandi di sistema;

  • istruzioni Sql;

  • Xml parsing;

  • accessi a risorse distribuite (es.: LDAP);

  • regexp, scripts, ecc.;

SQL injection

Di seguito un esempio di istruzione SQL affetto da injection vulnerability:

      SELECT * FROM users WHERE username='USER' AND password='PASSWD'

      String query = "SELECT * FROM users WHERE username='" + usr + "' AND password='" + pwd + "'";

      PASSWD = ' OR '1'='1  // querystring: pwd=%27%20OR%20%271%27=%271&usr=%27

      SELECT * FROM users WHERE username='' AND password='' OR '1'='1'

SQL injection prevention

Di seguito un esempio di codice java per prevenire le SQL injection in fase di autenticazione utente:


      String query = "SELECT password FROM users WHERE username='" + usr + "'";

      PreparedStatement stmt = connection.prepareStatement(query); 

      ResultSet rs = stmt.executeQuery();

      if (!rs.next()) {
         throw new SecurityException("User name inesistente");
      }

      if ( !rs.getString("password").equals(pwd) ) {
         throw new SecurityException("Password errata");
      }

XML injection

Non solo le istruzioni SQL sono soggette a injection, ma anche i documenti strutturati in codice XML:

    <item>
     <description>Ticket</description>
     <price>150.00</price>
     <quantity>1</quantity>
    </item>

    malformed input: '1</quantity><price>1.0</price><quantity>1'

    <item>
     <description>Ticket</description>
     <price>150.00</price>
     <quantity>1</quantity><price>1.0</price><quantity>1</quantity>
    </item>

Sensitive Data Leakage

Nel passaggio di dati tra i diversi sottosistemi possono verificarsi dispersioni di dati riservati (da non confondere con i data breaches).

Tra i dati riservati possiamo ricordare:

  • credit card numbers, passwords, private keys;

  • warnings, errors, logs, ecc.;

È sempre necessario filtrare e limitare l’accesso ai dati riservati che attraversano i vari trust boundaries.

Prevenire il data leakage

Di seguito una serie di misure di prevenzione dei data leakages:

  • mai inserire i dati riservati in forma hard-coded nel codice;

  • non serializzare i dati riservati senza prima averli cifrati;

  • non loggare i dati riservati al di fuori del loro trust domain;

  • non loggare i dati ricevuti in input dall’utente senza averli prima sanificati;

  • non mostrare all’utente il dump di errori o warning di sistema;

Prevenire i capabilities leakages

I capabilities leakages riguardano i diversi livelli di privilegio nell’esecuzione di codice su dati riservati che attraversano diversi trust boundaries;

  • non consentire a codice untrusted di terminare la JVM;

  • non incrementare l’accessibilità di metodi overridden o hidden;

  • non esporre le proprietà corrispondenti a dati riservati tra classi innestate;

  • minimizzare i livelli di privilegio prima di deserializzare i dati provenienti da contesti a più alto privilegio;

Denial of Service (DoS)

Una negazione di servizio si verifica a seguito dell’esaurimento delle risorse (computazionali, di rete, di spazio di archiviazione, ecc.) dovuto al comportamento malizioso dell’attaccante che sfrutta vulnerabilità del sistema;

come conseguenza di un DoS si possono verificare:

  • danni reputazionali;

  • compromissione dell’integrità dei dati;

  • danni hardware;

Prevenire i Denial of Service

Di seguito alcune misure di prevenzione dei DoS:

  • chiudere le risorse che non sono più necessarie;

  • rimozione dei temp files prima della fine del processo;

  • impedire che i processi si blocchino sui canali di I/O;

  • prevenire l’esaurimento di heap e stack memory space;

  • usare threads e connection pools per prevenire i picchi di traffico;

  • prevenire le corse critiche (race conditions) sulle risorse condivise;

Java Serialization

Java consente in maniera estensiva la serializzazione dello stato delle istanze di classe;
tale funzionalità è particolarmente rilevante nella RMI (Remote Method Invocation) e nei progetti di classe Enterprise (J2EE) che fanno uso di JavaBeans.

Un esempio di serializzazione e deserializzazione:


    ObjectOutputStream oos = new ObjectOutputStream( new FileOutputStream("SerializedOutput"));

    oos.writeObject(myInstance);
    oos.flush();

    ObjectInputStream ois = new ObjectInputStream( new FileInputStream("SerializedOutput"));

    deserialized = (InstanceClass) ois.readObject();

Serialization security threats

La serializzazione cattura tutti i campi di una istanza, inclusi quelli a visibilità non pubblica, i quali sono comunemente resi inaccessibili all’interno del codice delle classi.

Qualora il flusso di bytes corrisponde alla serializzazione dello stato dell’istanza fosse leggibile, un attaccante avrebbe possibilità di accedere ai corrispondenti dati riservati, determinando un data breach consistente sia in un accesso non autorizzato, che a una corruzione dei dati stessi, che dovrebbero essere di conseguenza considerati inattendibili.

Occorre pertanto introdurre opportune funzionalità di cifratura, sia nello spazio di archiviazione, che nel trasterimento tramite rete dei flussi di bytes corrispondenti agli oggetti serializzati, assicurandosi di applicare l’encryption nei diversi stati che i dati possono assumere (“in motion”, “in use” and “at rest).

Concurrency, visibility, memory

Tra le risorse condivise abbiamo la memoria di sistema; la memoria condivisa tra i threads è nota come heap memory.

Alcune variabili all’interno dei threads sono condivise grazie alla heap memory.

Le variabili considerate condivise sono:

  • instance fields;

  • static fields;

  • array elements

le variabili non condivise sono:

  • local variables;

  • method parameters;

  • exception handler parameters;

Le variabili condivise possono essere oggetto di race conditions

Principle of Least Privilege

Il principio stabilisce che ogni utente debba operare all’interno del sistema sfruttando il set minimo di privilegi necessari per portate a termine con successo un determinato task.

Tale principio riduce la superficie di attacco e le contribuisce a minimizzare le conseguenze negative di una vulnerabilità.

Le operazioni privilegiate dovrebbero essere limitate al più piccolo blocco di codice possibile.

Java mette a disposizione a tal fine il meccanismo AccessController, eseguendo il codice privilegiato all’interno del blocco contrassegnato dal metodo statico doPrivileged().


AccessController.doPrivileged(new PrivilegedAction() { 
   public Object run() {
          try {
               // privileged operations
          } catch (Throwable t) {}
          return null; 
   } });

Security Managers e AccessController

I Security Managers costituiscono delle policy per il controllo di accesso, che vengono concretamente implementate con gli AccessController.

Un security manager specifica quali tra le azioni potenzialmente pericolose sono permesse.

Ogni azione non consentita eseguita dal codice dà origine a una SecurityException.

La classe Java SecurityManager incapsula una determinata security policy.

Per istanziare un security manager, il codice deve possedere il permesso di runtime createSecurityManager, e setSecurityManager per installarlo.

Security Manager example


public final void readFile() { 
  try {
      SecurityManager sm = System.getSecurityManager();
      if (sm != null) { 
         // Check for permission to read file
         sm.checkRead("/tmp/file");
      }
      // Access the file
  } catch (SecurityException se) { }
} 

ClassLoaders

Il bytecode java viene dinamicamente caricato all’interno della JVM tramite la class ClassLoader.

Tutti i class loaders ereditano da SecureClassLoader, che effettua dei security checks sui metodi delle classi caricate mediante il metodo getPermissions(), che ritorna i privilegi associati alle classi caricate dal class loader.