Securitatea aplicatiilor Java

Arhitectura de securitate Java

In prezent, arhitectura de securitate Java include un set de interfete de programare a aplicatiilor (API), instrumente, implementari ale celor mai utilizati algoritmi de securitate, mecanisme si protocoale. Toate acestea furnizeaza dezvoltatorului un mediu de securitate cuprinzator pentru scrierea aplicatiilor precum si administratorilor un set de instrumente pentru securizarea aplicatiilor. In prezent, limbajul Java este construit pentru tipuri de date sigure, usurinta in utilizare, management automat al memoriei, garbage collection, verificarea tablourilor. Toate acestea permit scrierea unui cod robust si sigur. Java foloseste diferiti modificatori de acces. Compilatorul translateaza programul Java bytecode care este independent de masina. Bytecodul este verificat conform Java Language Specification.

Platforma Java include criptografia, infrastructura de chei publice, autentificarea, securizarea comunicatiei si controlul accesului. Toate aceste respecta urmatoarele principii:

  1. Independenta implementarii. Aplicatiile, de regula, nu trebuie sa implementeze propria securitate. Ele trebuie sa utilizeze serviciile de securitate oferite de platforma Java. Serviciile de securitate sunt oferite de provideri prin intermediul unei interfete standard. O aplicatie poate utiliza mai multe servicii de securitate de la provideri independenti.
  2. Interoperabilitatea implementarii. Providerii sunt interoperabili intr-o aplicatie. Cu alte cuvinte, o aplicatie nu este construita pentru un anumit provider dar, nici un provider nu ofera un serviciu de securitate pentru o anumita aplicatie.
  3. Extensibilitatea algoritmului. Desi Java vine cu un numar de provideri ce implementeaza un set de baza de securitate, aplicatiile pot folosi standarde proprietare sau care nu sunt inca implementate.

Provideri de securitate

Clasa java.security.Provider incapsuleaza notiunea de provider de securitate in platforma Java. Ea specifica numele providerului si lista de servicii de securitate implementate. Aplicatiile se bazeaza pe metoda getInstance pentru a obtine servicii de securitate de la provider. Prin exemplul urmator, o aplicatie poate obtine implementarea MD5:

MessageDigest md=MessageDigest.getInstance(„MD5”);

Daca dorim sa obtinem implementarea unui anumit provider, o putem face astfel:

MessageDigest md=MessageDigest.getInstance(„MD5”, „ProviderC”);

Figurile urmatoare ilustreaza optiunile pentru obtinerea implementarii MD5. Ambele figuri arata trei provideri care implementeaza algoritmul mesajelor. In figura 1, o aplicatie cere un algoritm cu implementare MD5 fara a specifica numele providerului. Providerii sunt cautati in ordinea preferintei. In figura 2 aplicatia cere un algoritm cu implementare MD5 de la un provider specificat.

Platforma Java, implementarea Oracle, include un numar de provideri pre-configurati care implementeaza un set de servicii de securitate de baza care pot fi utilizate de aplicatii. Alti producatori pot include diferite seturi de provideri care pot incapsula seturi specifice de servicii de securitate.

Criptografia

Java Cryptography Extension este un framework pentru accesarea si dezvoltarea functiunilor criptografice pentru platforma Java. El include numeroase servicii criptografice, cum ar fi:

  • Message digest algorithms
  • Digital signature algorithms
  • Symmetric bulk encryption
  • Symmetric stream encryption
  • Asymmetric encryption
  • Password-based encryption
  • Elliptic Curve Cryptography
  • Key agreement algorithms
  • Key generators
  • Message Authentification Codes
  • (Pseudo-)Random number generators

API-ul pentru criptografie este organizat in doua pachete distincte. Unul este java.security si contine clase care nu sunt subiectul controalelor de export. Al doilea pachet este javax.crypto care contine clase ce sunt subiectul controalelor de export.

Interfata criptografica este bazata pe furnizor, permitand multiple si interoperabile implementari. Unii provideri pot realiza operatiuni de criptografie in software, altii in chei hardware.

Platforma Java include provideri pre-inclusi pentru majoritatea algoritmilor criptografici, cum ar fi RSA, DSA, DES, AES, ARCFOUR, MD5, SHA-1 si Diffie-Hellman. Acesti provideri pre-inclusi implementeaza algoritmii criptografici in codul Java.

Java Cryptography Extension (JCE) ofera suport pentru incriptare, decriptare, key agreement, Message Authentication Code (MAC) si alte servicii criptografice. Librariile JCE sunt localizate in javax.crypto package.

Pentru a demonstra utilizarea JCE API, programul urmator incripteaza o serie de obiecte si apoi le decripteaza inapoi. Exemplul de mai jos utilizeaza algoritmul DES:

import java.io.*; 
import javax.crypto.*; 
import javax.crypto.spec.*; 
import java.security.*; 
import java.security.spec.*; 
import java.util.*; 
 
public class EncryptTest { 
 
  public static void main(String args[]) { 
 
    File desFile = new File("out.des"); 
 
    // Creaza datele  
    Map map = new TreeMap(System.getProperties()); 
    int number = map.size(); 
 
    try { 
 
      // Creaza cheia
      KeyGenerator kg = KeyGenerator.getInstance("DES"); 
      SecretKey secretKey = kg.generateKey(); 
 
      // Creaza Cipher
      Cipher desCipher = 
        Cipher.getInstance("DES/ECB/PKCS5Padding"); 
      desCipher.init(Cipher.ENCRYPT_MODE, secretKey); 
 
      // Creaza fluxul 
      FileOutputStream fos = new FileOutputStream(desFile); 
      BufferedOutputStream bos = new BufferedOutputStream(fos); 
      CipherOutputStream cos = new CipherOutputStream(bos, 
        desCipher); 
      ObjectOutputStream oos = new ObjectOutputStream(cos); 
 
      // Scrie obiectele 
      oos.writeObject(map); 
      oos.writeInt(number); 
      oos.flush(); 
      oos.close(); 
       // Schimba modul cipher
      desCipher.init(Cipher.DECRYPT_MODE, secretKey); 
 
      // Creaza fluxul
      FileInputStream fis = new FileInputStream(desFile); 
      BufferedInputStream bis = new BufferedInputStream(fis); 
      CipherInputStream cis = new CipherInputStream(bis, 
        desCipher); 
      ObjectInputStream ois = new ObjectInputStream(cis); 
       // Citeste obiectele
      Map map2 = (Map)ois.readObject(); 
      int number2 = ois.readInt(); 
      ois.close(); 
 
      // Compara cu originalul
      if (map.equals(map2) && (map.size() == number2)) { 
        System.out.println("Everything read back out okay."); 
      } else { 
        System.out.println("Problems during 
          encryption/decryption process."); 
      } 
    } catch (NoSuchPaddingException e) { 
      System.err.println("Padding problem: " + e); 
    } catch (NoSuchAlgorithmException e) { 
      System.err.println("Invalid algorithm: " + e); 
    } catch (InvalidKeyException e) { 
      System.err.println("Invalid key: " + e); 
    } catch (IOException e) { 
      System.err.println("I/O Problem: " + e); 
    } catch (ClassNotFoundException e) { 
      System.err.println("Class loading Problem: " + e); 
    } finally { 
      if (desFile.exists()) { 
        desFile.delete(); 
      }  
    } 
  } 
} 

Chei si certificate

Public Key Infrastructure (PKI) este termenul folosit pentru cadrul care asigura schimbul securizat al informatiilor bazat pe criptografia cu cheie publica. El permite diferitelor entitati sa foloseasca certificate digitale si furnizeaza mijloace de verificare a acestor certificate.

Platforma Java include API si furnizeaza suport pentru certificatele X.509 si pentru liste de revocare a certificatelor (CRLs). Clasele PKI sunt localizate in pachetele java.security si java.security.cert.

Platforma Java furnizeaza un mediu de stocare pe termen lung a cheilor criptografice si a certificatelor. In mode special, clasa java.security.KeyStore reprezinta un keystore, un depozit securizat al cheilor criptografice si/sau a certificatelor sigure iar clasa java.security.cert.CertStore reprezinta un certification store, un public si potential depozit pentru certificatele nesigure. Platforma Java include o cheie speciala JKS (cacerts), care contine un numar de certificate cunoscute si sigure CA. Documentatia keytool cuprinde lista cu certificatele incluse in cacerts.

De asemenea, platforma Java include un certificat de tip LDAP care este cunoscut in memorie ca un tip de stocare Collection (pentru accesarea certificatelor administrate de obiectul java.util.Collection). Instrumentele PKI permit lucrul cu chei, certificate si key stores. Instrumentul keytool este utilizat pentru a crea si administra key stores. El poate:

  • crea perechi de chei publice/private
  • afisa, importa, exporta certificate stocate in fisiere de tip X.509 v1,v2 si v.3
  • crea certificate autosemnate
  • descarca certificate cerute a fi trimise la CAs
  • importa certificatele primite
  • desemna certificatele ca fiind de incredere

Instrumentul jarsigner este utilizat pentru a semna fisierele jar, sau pentru a verifica semnaturile fisierelor jar.

Autentificarea

Autentificarea este un proces de determinare a identitatii utilizatorului. In Java runtime, acesta este un proces de identificare a utilizatorului care excuta un program Java. Platforma Java furnizeaza un API care permite aplicatiilor sa realizeze autentificarea prin module pluggable. Aplicatiile apeleaza clasa LoginContext aflata in pachetul javax.security.auth.login. De cand aplicatiile comunica cu API-ul standard LoginContext ele raman independente de modulele plug-in. Modulele noi sau updatate pot fi incluse in aplicatii fara a fi necesar modificarea aplicatiei.

Figura urmatoare ilustreaza independenta intre aplicatii si modulele de logare:

FIGURA

Platforma Java furnizeaza pre-incluse LoginModules, toate aflate in pachetul com.sun.security.auth.module:

  • Krb5LoginModule, pentru autentificarea utilizand protocolul Kerberos
  • JndiLoginModule, pentru autentificarea utilizator/parola utilizand LDAP sau NIS.
  • KeyStoreLoginModule, pentru logarea in orice tip de stocare a cheii, inclusiv PKCS#11

Autentificarea poate fi realizata si in timpul stabilirii canalului de comunicatie securizate intre doi parteneri egali.

Securizarea comunicatiilor

Datele care sunt transmise prin retea pot fi accesate de cineva care nu este autorizat. Daca datele includ informatii private, cum ar fi parole si numere de credit card-uri, trebuie parcursi niste pasi pentru ca datele sa devina neinteligibile partilor neautorizate.

Platforma Java furnizeaza suport API pentru un numar de protocoale standard de comunicatie. Astfel, Java API implementeaza protocoalele SSL si TLS care includ functionalitate pentru incriptarea datelor, integritatea mesajelor, autentificarea serverelor si autentificarea optionala a clientilor. Aplicatiile pot utiliza SSL/TLS pentru a realiza transfer securizat de date intre 2 parteneri egali deasupra oricarui protocol cum ar fi HTTP peste TCP/IP.

Clasa javax.net.ssl.SSLSocket reprezinta un socket de retea care incapsuleaza suport SSL/TLS peste socketul normal (java.net.Socket).

Platforma Java include suport API pentru pluggable key managers si trust managers. Un key managers este incapsulat de clasa javax.net.ssl.KeyManager si adminstreaza cheile pentru a realiza autentificarea. Un trust manager este incapsulat de clasa TrustManager.

Simple Authentication and Security Layer (SASL) este un standard internet care specifica protocolul pentru autentificare si, optional, un strat de securitate in client si server la nivel de aplicatie. SASL defineste ce date de autentificare pot fi schimbate, dar nu specifica continutul datelor. Exista un numar de mecanisme standard definite de comunitatea Internet pentru diferite nivele de securitate si scenarii de desfasurare.

API-ul Java SASL defineste clase si interfete pentru aplicatii care utilizeaza mecanismul SASL. Aplicatiile pot selecta mecanismul pe care doreste sa-l utilizeze pe baza caracteristicilor de securitate. API-ul suporta aplicatiile client si server. Clasa javax.security.sasl.Sasl este utilizata pentru a crea obiecte SaslClient si SaslServer.

Implementarea mecanismului SASL este furnizata de providerul pachetelor. Fiecare provider poate suporta unul sau mai multe mecanisme SASL si este inregistrat si invocat prin arhitectura standard a providerului.

Platforma Java contine API cu legaturi ale limbajului Java pentru Generic Security Service Application Programming Interface (GSS-API). GSS-API ofera programatorilor de aplicatii acces uniform la servicii de securitate si la o mecanisme de securitate. Java GSS-API necesita mecanismul Kerberos V5 iar platforma Java preinclude o implementare a acestui mecanism.

Inainte ca doua aplicatii sa utilizeze Java GSS-API pentru a schimba mesaje sigure intre ele, trebuie sa stabileasca un context de securitate punctual. Contextul incapsuleaza informatiile de stare partajate, de ex. cheile criptografice. Ambele aplicatii creaza si utilizeaza un obiect org.ietf.jgss.GssContext pentru a stabili si mentine informatiile comune. Imediat ce contextul de securitate este stabilit el poate fi utiliza pentru a pregati schimbul securizat de mesaje .

Reguli de securitate in programarea Java

In continuare, vom enunta cateva reguli/constatari privind securitatea in programarea Java, vehiculate de diversi autori:

  • Limitarea accesului la clase, metode si variabile. In special atributele trebuie declarate private. Alocarea altor modificatori de acces mai permisivi trebuie sa se faca pentru motive foarte clare.
  • Utilizarea modificatorului final pentru clase sau metode. Trebuie sa existe un motiv bine documentat pentru ca o clasa sa fie permis a fi extinsa. Un atacator va incerca sa extinda o clasa mai ales daca aceasta este publica.
  • Clasele, metodele si variabilele care nu sunt publice, private sau protected, sunt accesibile din acelasi pachet. Aceasta nu asigura o securitate suficienta, pentru ca un atacator poate introduce o clasa in pachet si astfel va putea accesa clasa initiala.
  • Este nerecomanda folosirea claselor interioare. Cu toate ca in documentatia Java se spune ca clasele interioare pot fi accesate doar de clasa exterioara, acest lucru nu este adevarat. Bytecod-ul Java nu cunoaste conceptul de clase interioare, de fapt compilatorul translateaza o clasa interioara intr-o clasa separata, care poate fi accesata din tot pachetul.
  • Codul care nu este semnat nu necesita privilegii speciale deci este mult mai usor de distrus sau modificat.
  • Daca codul trebuie semnat, acesta trebuie pus intr-un fisier arhiva, altfel un atacator poate scrie un applet sau o librarie care sa se lege la clasele semnate prin clase „rau-voitoare”.
  • Clasele nu trebuie sa fie clonabile. Mecanismul de clonare Java permite unui atacator sa defineasca o subclasa a clasei noastre si sa o faca sa implementeze java.lang.Clonable. Noua instanta poate fi construita prin copierea imaginii memoriei a obiectului existent, ceea ce nu este acceptabil de cele mai multe ori.
  • Clasele nu trebuie sa fie serializabile. Aceasta impiedica atacatorii sa acceseze starea interna a obiectelor noastre. Un atacator poate serializa un obiect intr-un tablou de tip byte care poate fi citit, fapt care permite inspectarea starii unui obiect, inclusiv atributele marcate private. Pentru a realiza acest obiectiv declaram metoda writeObject astfel:
private final void writeObject(ObjectOutputStream out) throws java.io.IOException {
        throw new java.io.IOException("Object cannot be serialized");
}
  • Clasele nu trebuie sa fie deserializabile. Atacatorii pot crea o secventa de bytes care pot deserializa o instanta a clasei noastre, fapt care poate duce la pierderea controlului asupra obiectului. Pentru a realiza acest obiectiv declaram metoda readObject astfel:
private final void readObject(ObjectOutputStream in) throws java.io.IOException {
        throw new java.io.IOException("Object cannot be deserialized");
}
  • Nu se stocheaza informatii secrete in codul aplicatiei (ex: cheile criptografice). „Code obfuscation” este o cale pentru stocarea informatiilor secrete in cod insa nu este infailibila.

In concluzie, nu exista o metoda perfecta pentru a rezolva toate problemele de securitate in Java, dar utilizarea metodelor descrise mai sus pot face codul mult mai sigur, minimizand riscurile.