TSM - Dezvoltarea aplicațiilor securizate în Java

Silviu Dumitrescu - Line manager@Telenav


Vom începe acest articol cu câteva considerente generale despre securitate. Astfel, scopul securizării calculatoarelor este acela de a proteja informațiile existente pe acestea de furt, de corupere sau dezastre naturale.

Securitatea trebuie înțeleasă ca o măsură de compromis. De exemplu, cea mai bună metodă de a face o aplicație complet securizată în Internet este să nu o conectăm la Internet.

Unul dintre aspectele importante ale securității este confidențialitatea, care reprezintă ascunderea surselor de informații. Mecanismele folosite pentru realizarea confidențialității sunt: încriptarea, folosirea parolelor și controlul accesului adică acordarea accesului la resurse unui număr limitat de persoane.

Un alt aspect este integritatea, care înseamnă că datele sunt protejate față de modificări neautorizate. Aceasta este realizată de obicei prin autentificare. User-ul trebuie să furnizeze credențiale (username si password). În plus, sistemele de detecție trebuie să fie folosite în cazul în care sistemul de autentificare eșuează. Acest sistem este format din loguri de acces și pattern-i de analiză.

Un ultim aspect este disponibilitatea, care reprezintă abilitatea de a utiliza un sistem sau o resursă la nevoie.

Cel mai simplu mod în care un sistem este vulnerabil îl reprezintă atacurile de refuzare a serviciilor. Acestea blochează accesul la sistem al utilizatorilor sau reduce nivelul de performanță al sistemului. Sistemul ar trebui să fie atât de flexibil încât să detecteze aceste atacuri și să răspundă la ele.

Aspecte de securitate la nivelul software-urilor

Orice sistem ce conține informații confidențiale este foarte probabil o țintă a atacatorilor.

Câteva dintre conceptele fundamentale de securitate sunt:

Tipuri de amenințări de securitate

Putem să împărțim amenințările în următoarele categorii:

Injecția și incluziunea reprezintă un atac ce determină un program să interpreteze date într-un mod neașteptat. De aceea, orice date venite dintr-o sursă nesigură trebuie validate. Principalele forme de atac sunt:

Vulnerabilitățile XSS apar atunci când:

Următorul exemplu ilustrează un atac XSS:

<%@page contentType=”text/html” pageEncoding=”UTF-8”%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv=”Content-Type”
content=”text/html; charset=UTF-8”>
<title>Login Page</title>
</head>
<body>
<h2>Bine ati venit</h2>
<p>Va rog sa va logati</p>
<form method=”GET”
action=”/XssExample/ProcessForm”>
<b>Login Name: </b>
<input type=”text” size=”12”
maxlength=”12” name=”userName” />
<br/>
<b>Password: </b>
<input type=”text” size=”12”
maxlength=”12” name=”password” />
<br/>
<b>Locatia: </b>
<input type=”text” size=”12”
name=”location” /><br/>
<input type=”submit” value=”Submit” />
<input type=”reset” value=”Reset” />
</form>
<p><a href=”http://localhost:8080/XSS/ProcessForm?userName=Bob&password=Smith&location=</p>%3CScript%20Language%3D%22Javascript%22%3Ealert(%22Ai%20fost%20atacat!%22)%3B%3C%2FScript%3E”>Hacked URL</a></p>
<p>URL Script text: %3CScript%20Language%3D%22Javascript%22%3Ealert(%22vei%20fi%20atacat!%22)%3B%3C%2FScript%3E</p>
</body>
</html>

Respectiv servletul:

@WebServlet("/ProcessForm")
public class ProcessForm extends HttpServlet {
private static final long
serialVersionUID = -5014955266331211217L;

protected void processRequest(
HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {

response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
try {
out.println("<html>");
out.println("<head>");
out.println(
"<title>Servlet de procesare</title>");
out.println("</head>");
out.println("<body>");
out.println("<h2>Prima pagina</h2>");
out.println("<p>sunteti logat ca: " +
request.getParameter("userName") + "</p>");
out.println("<p>si sunteti in: " +
request.getParameter("location") + "</p>");
out.println("</body>");
out.println("</html>");
} finally {
out.close();
}
}

@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}

@Override
protected void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}

@Override
public String getServletInfo() {
return "Servlet-ul meu";
}
}

SQL Injection se bazează pe date nefiltrate pentru a modifica rezultatele SQL. Fie următorul cod:

ResultSet rs = stmt.executeQuery ("SELECT * FROM DEMO.Table1 WHERE NAME='" + nameParam + "' AND AGE ='" + ageParam + "'");

Dacă atacatorul trimite: 'valori' OR 'a' = 'a', acesta va face predicatul de selecție adevărat, ceea ce este echivalent cu:

ResultSet rs = stmt.executeQuery ("SELECT * FROM DEMO.Table1");

Prin aceasta atacatorul poate accesa informații confidențiale sau chiar modifica date din baza de date. De aceea orice input trebuie filtrat înainte de utilizare.

OS command injection se bazează pe date nefiltrate ce modifică o comandă a sistemului de operare.

Fie următorul exemplu:

public class ListHomeDir {
  public static void main(String[] args) {
    String userName = "Silviu";
    String curLine = "";
      try {
    Process p = Runtime.getRuntime().exec(
      "cmd /c dir C:\\Users\\" + userName);
    BufferedReader stdInput = new 
          BufferedReader(new InputStreamReader(
          p.getInputStream()));

        BufferedReader stdError = new 
           BufferedReader(new InputStreamReader(
           p.getErrorStream()));

 System.out.println("Home directory este:");
 while ((curLine = stdInput.readLine()) != null) {
   System.out.println(curLine);
 }
 if (stdError.ready()) {
  System.out.println("eroare:");
 }
 while ((curLine = stdError.readLine()) != null) {
   System.out.println(curLine);
}
 System.exit(0);
} catch (IOException e) {
  System.out.println("exceptie: ");
  System.out.println(e.getMessage());
  System.exit(-1);
   }
 }
}

În exemplu dorim să obținem un listing al unui director. Atacatorul poate trimite: username;&&del *.*;, ceea ce va determina pierderea de date.

Fie următorul exemplu de format necontrolat de string-uri:

public class Main {
static Calendar c = new GregorianCalendar(1995, GregorianCalendar.MAY, 23);

public static void main(String[] args) {String param = "%1$tY";
        System.out.printf(param + " nu se potriveste! A fost emis in ziua %1$te \n", c);
    }
}

În acest cod programatorul încearcă să printeze rezultatele atunci când două valori nu se potrivesc. Problema poate apare atunci când este trimis un string format în loc de lună. Atacatorul își poate da seama de an, spre exemplu, atunci când un card expiră.

Din punct de vedere al managementului de resurse avem:

Fie următorul exemplu:

public class FileException {
    Properties appProps = new Properties();
    public static void main(String[] args) {
      FileException app = new FileException();
          app.readProps("AppConfig.properties");
      app.printProps();
    }

    public void readProps(String fileName) {
    try {
     appProps.load(new FileInputStream(fileName));
     } catch (IOException e) {
    System.out.println("Nu putem gasi fisierul "+ 
      "de configurare: " + e.getMessage());
    e.printStackTrace();
    System.exit(-1);
    }
     } 
  public void printProps() {
    appProps.list(System.out);
  }
}

Sistemul nu ar trebui să furnizeze potențialilor atacatori locația exactă a fișierului de configurare al aplicației.

Informațiile confidențiale ar trebui să fie disponibile spre citire doar într-un context limitat, să nu fie disponibile pentru manipulare, să se furnizeze utilizatorilor doar informațiile de care au nevoie, informațiile să nu fie hard cod-ate în surse.

Datele confidențiale nu ar trebui incluse în excepții sau fișiere de log. De asemenea, nu ar trebui să hard cod-ăm username-ul și parola în codul sursă. Ar trebui folosit un fișier de proprietăți pentru a stoca acest tip de informații.

Dăm mai jos un exemplu de creare și folosire a unui fișier de log.

public class BasicLogging {
    Logger logger = Logger.getLogger("com.example.BasicLogging");

    public void logMessages(){     
        logger.severe("A aparut o problema severa");
        logger.warning("Avertisment");
        logger.log(Level.INFO,"Informatie utila");
        logger.config("Informatii despre CONFIG");
    }

    public static void main(String[] args) {
        BasicLogging bl = new BasicLogging();
        bl.logger = Logger.getLogger("com.example"+
    ".BasicLogging");

        try {
         bl.logger.addHandler(new FileHandler(
          "Basic.log"));
            bl.logger.setLevel(Level.INFO);
            bl.logMessages();
        } catch (IOException e){
            e.getMessage();
        }
    }
}

Vă dorim lectură plăcută și așteptăm cu interes întrebările voastre!