Developing Secure Applications in Java

Previously published on Today Software Magazine.

We will begin this article by some general considerations regarding security. Thus, the aim of computer security is to protect the information stored on them against theft, corruption or natural disasters while accessing it. Security must be understood as a compromise solution. For instance, the best way to create a completely secure application on the Internet is not to connect it to the Internet.

One of the most important aspects of security is confidentiality, which represents hiding the information sources. The mechanisms used for ensuring confidentiality are: encrypting, using passwords and access control (giving access to resources to a limited number of people).

Another aspect is integrity, which means that the data is protected against unauthorized alterations. This is usually ensured by authentication. The user must provide credentials (username and password). Moreover, the detection systems should be used in case the authentication system fails. This system is made of access logs and analysis patterns.

A last aspect is availability, which represents the ability to use a system or a resource when needed.

The easiest way in which a system is vulnerable is represented by the attacks of denial of the services. They block the user’s access to the system or reduce the performance level of the system. The system should be so flexible as to detect these attacks and respond to them.

Security aspects at software level

Any system containing private information is very likely to become a target for the attackers. Some of the fundamental concepts of security are:

  • Secured APIs: it is much easier to conceive a secure code right from the beginning. The attempt to secure existing code is difficult and it generates errors.
  • Duplication: duplicate code may not be consistently treated on copying. Furthermore, it also breaches the Agile programming principle, Don’t Repeat Yourself (DRY).
  • Limiting preferential claims: if the code operates with limited privileges, then default exploitation is most likely to be prevented.
  • Trust boundaries: establishing the limits between the different parts of the application. For instance, any data that come from a web browser into a web application must be checked before being used.
  • Security checking: carrying out security checking in a few defined points and returning an object that the client code retains, so that there will be no further need of subsequent checking.
  • Encapsulation: using interfaces; the fields should be private and accessories should be avoided.

Types of security threats

We can divide the threats into the following categories:

  • Injection and inclusion
  • Resource management (buffer overflow, denial of service)
  • Private (confidential) information
  • Accessibility and extensibility
  • Mutability

Injection and inclusion represent an attack which determines a program to interpret the data in an unexpected way. Therefore, any data coming from an uncertain source must be validated. The main forms of attack are:

  • Cross-site scripting (XSS)
  • SQL Injection
  • OS Command Injection
  • Strings formatted in an uncontrolled manner

The XSS vulnerabilities appear when:

  • data coming from unreliable sources enter a web application the web application dynamically generates a web page that contains unreliable data
  • while generating the web page, the application does not prevent the data from the content run by the browser, such as JavaScript, HTML tags, HTML attributes, mouse events, Flash or ActiveX
  • when using a web browser, the victim visits the generated page which contains a malicious script that has been injected by unreliable data
  • the script comes from a web page that has been sent by the web server, the victim runs the malicious script in the framework of the web server domain
  • the victim breaches the policy of a web browser which says that the scripts in a domain should not be able to access resources or run code in another domain.

The following example illustrates a XSS attack:

<%@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>

Respectively the servlet:

@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>Login Page</title>");            
            out.println("</head>");
            out.println("<body>");
            out.println("<h2>Please login: </h2>");
            out.println("<p>Please login: " + 
   request.getParameter("userName") + "</p>");
   out.println("<p>Login Name: " + 
       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 is based on unfiltered data in order to alter the SQL results. Let’s consider the following code:

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

If the attacker sends: ‘values’ OR ‘a’ = ‘a’, it will make the selection predicate true, which is equivalent to:

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

By doing this, the attacker can access private information or can even modify data in the data base. This is why any input must be filtered before being used.

OS command injection is based on unfiltered data which alter the command of the operating system. Let’s consider the following example:

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 is:");
 while ((curLine = stdInput.readLine()) != null) {
   System.out.println(curLine);
 }
 if (stdError.ready()) {
  System.out.println("error:");
 }
 while ((curLine = stdError.readLine()) != null) {
   System.out.println(curLine);
}
 System.exit(0);
} catch (IOException e) {
  System.out.println("exception: ");
  System.out.println(e.getMessage());
  System.exit(-1);
   }
 }
}

In the example, we wish to obtain a listing of a director. The attacker can send:

username;&& del *.*;, which can cause the loss of data.

Let’s consider the following example of uncontrolled format of strings:

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 + "Error !!! %1$te n", c);
    }
}

In this code, the programmer tries to print the results when two values do not match. The problem appears when a formed string is sent instead of a month. The attacker can figure out the year, for instance, when a card becomes out of date.

From the point of view of resource management, we have:

  • buffer overflows: copying an input buffer into an output buffer without checking the size. Its result is the fact that an attacker can run code outside a normal program. Java is immune to this type of attack, as it owns an automated management of memory.
  • denial of service: it is still possible in Java, through the inadequate usage of resources. Here are a few examples of potential denial-of-service attacks:
    • zip bomb: a zip file that is relatively small, but includes many other zips in it. Unzipping the files can block the processor and it can engulf a big storage space. As a protection measure we can limit the size and processing that can be done inside a resource such as this.
    • billion laughs attack: if we are using the DOM API for a XML file, we must upload the entire file into the memory. Such an attack can engulf the entire memory.
    • Path: is a language for interrogations and crossings of XML files. Some interrogations can be recursive and can return a bigger volume of data than the one expected.
    • Object Graph: an object graph is built by parsing a text or a binary stream; it can require much more memory than the original data.

Let’s consider the following example:

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("Cannot find the file "+ 
      "de configurare: " + e.getMessage());
    e.printStackTrace();
    System.exit(-1);
    }
     } 
  public void printProps() {
    appProps.list(System.out);
  }
}

The system should not provide the potential attackers with the exact location of the configuration file of the application.

Confidential information should be available to reading only in a limited context; they should not be available for manipulation; users should be provided only with the information they need; the information should not be hard coded in the sources.

Private data should not be included in exceptions of log files. Also, we shouldn’t hard code the username and password into the source code. We should use an attributes file in order to store this type of information.

Here is an example of creating and using a log file.

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

    public void logMessages(){     
        logger.severe("Critical error");
        logger.warning("Advertisment");
        logger.log(Level.INFO,"Usefull info");
        logger.config("Info about 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();
        }
    }
}

We hope you have enjoyed reading this article and we are looking forward to your questions!