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.
Orice sistem ce conține informații confidențiale este foarte probabil o țintă a atacatorilor.
Câteva dintre conceptele fundamentale de securitate sunt:
API-uri securizate: este mult mai ușor să concepem un cod securizat încă de la început. Încercarea de a securiza codul existent este dificil și generator de erori.
Duplicarea: codul duplicat este posibil a nu fi tratat consistent la copiere. Mai mult, aceasta violează și principiul programării Agile, Don't Repeat Yourself (DRY).
Restricționarea privilegiilor: când codul operează cu privilegii reduse, atunci exploatarea defectelor este dejucată.
Trust boundaries: stabilirea limitelor între diferitele părți ale aplicației. Spre exemplu, orice date care vin dintr-un browser web, într-o aplicație web, trebuie verificate înaintea utilizării.
Verificarea securității: efectuarea verificărilor de securitate în câteva puncte definite și returnarea unui obiect pe care codul client îl reține, astfel încât să nu mai fie nevoie de verificări ulterioare.
Putem să împărțim amenințările în următoarele categorii:
Injecția și incluziunea;
Gestionarea resurselor (buffer overflow, denial of service)Ș
Informațiile confidențiale;
Accesibilitatea și extensibilitatea;
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:
Cross-site scripting (XSS),
SQL Injection,
OS Command Injection,
Vulnerabilitățile XSS apar atunci când:
date provenind din surse lipsite de încredere intră într-o aplicație web.
aplicația web generează dinamic o pagină web ce conține date nesigure.
pe timpul generării paginii web aplicația nu previne datele din conținutul executat de browser, precum JavaScript, tag-uri HTML, atribute HTML, evenimente ale mouse-ului, Flash sau ActiveX.
utilizând un web browser, victima vizitează pagina generată, ce conține un script malițios ce a fost injectat de date nesigure.
script-ul care vine dintr-o pagină web ce a fost trimisă de serverul web, victima execută script-ul malițios în contextul domeniului serverului web.
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:
buffer overflows: copierea unui buffer de intrare într-un buffer de ieșire fără verificarea dimensiunii. Are ca rezultat faptul că un atacator poate executa cod în afara unui program normal. Java este imună la acest tip de atac deoarece deține un management automat al memoriei.
denial of service: este încă posibil în Java prin folosirea nepotrivită a resurselor. Iată câteva exemple de atacuri potențiale denial-of-service:
zip bomb: un fișier zip ce este relativ mic, dar care include multe alte zip-uri în el. De-zip-area fișierelor poate bloca procesorul și înghiți un spațiu mare de memorie. Ca măsură de protecție putem limita dimensiunea și procesarea ce pot fi făcute într-o resursă ca aceasta.
billion laughs attack: dacă utilizăm API-ul DOM pentru un document XML trebuie să încărcăm întregul document în memorie. Un atac ca acesta poate înghiți întreaga memorie.
XPath: este un limbaj pentru interogări și traversări de documente XML. Unele interogări pot fi recursive și pot returna un volum de date mai mare decât cel așteptat.
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!