Secure coding

Securing Your Java Code 2

Coder Geek
December 20, 2013 by
Coder Geek

For Part 3

Learn Secure Coding

Learn Secure Coding

Build your secure coding skills in C/C++, iOS, Java, .NET, Node.js, PHP and other languages.

SQL Injection

SQL injection occurs when a user sends malicious data to an interpreter as an SQL query. The attacker sends simple text-based attacks that exploit the targeted interpreter. An attack with an SQL string in it can be used to bypass authentication of data from database tables. It can also result loss of data.

Vulnerable Code:

[java]

<%@page import="java.sql.*"%>

<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">

<title> </title>

</head>

<body>

<%

String user = request.getParameter("user");

String pass = request.getParameter("pass");

Class.forName("com.mysql.jdbc.Driver");

Connection con = (Connection) DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "root" ,"");

Statement st= con.createStatement();

ResultSet rs=st.executeQuery("select * from users where username='"+user+"' and password='"+pass+"' limit 0,1");

if(rs.next())

{

out.println("Login success");

}

else

{

out.println("Login failed");

}

%>

</body>

</html>

[/java]

In the above vulnerable code, the developer has used the Statement class to create an SQL statement and execute it to fetch a valid username-password pair. But this Java class does not implement proper SQL injection prevention techniques, due to which an attacker can send malicious SQL code to bypass the login check. A user trying to enter the username as admin and password as junk or 1=1 will be able to login. It bypassed the login because the SQL query would evaluate to true for this password because of 1=1.

Patched Code:

[java]

<%@page import="java.sql.*"%>

<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">

<title></title>

</head>

<body>

<%

String user = request.getParameter("user");

String pass = request.getParameter("pass");

Class.forName("com.mysql.jdbc.Driver");

Connection con = (Connection) DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "root" , "");

PreparedStatement ps=(PreparedStatement) con.prepareStatement("select * from users where username=? and password=? limit 0,1");

ps.setString(1,user);

ps.setString(2,pass);

ResultSet rs=ps.executeQuery();

if(rs.next())

{

out.println("Login success");

}

else

{

out.println("Login failed");

}

%>

</body>

</html>

[/java]

The patched code for an SQL injection flaw is to use the PreparedStatement class (recommended by OWASP) because it automatically escapes the input string and the user input is treated as a single string. As a result, the injection string in the input is treated as a string and SQL injection is prevented. For the same input for login bypass, the patched code responds with a failure message.

Broken Authentication and Session Management

Broken Authentication

This vulnerability occurs when any sensitive data is not stored securely. Sensitive information such as passwords, credit card information, and bank details should be stored either in encrypted or hashed format inside a database or storage space so that, even if the data is compromised, it cannot be deciphered easily. Sensitive data should either be encrypted or hashed when stored somewhere.

Vulnerable Code:

[java]

<%@page import="java.sql.*"%>

<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">

<title></title>

</head>

<body>

<%

String user=request.getParameter("user");

String pass=request.getParameter("pass");

Class.forName("com.mysql.jdbc.Driver");

Connection con=(Connection)DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "root" ,"");

PreparedStatement ps = (PreparedStatement) con.prepareStatement("insert into users_crypt values(?,?)");

ps.setString(1,user);

ps.setString(1,pass);

int res = ps.executeUpdate();

if(res>0)

{

out.println("Register success");

}

else

{

out.println("Register failed");

}

%>

</body>

</html>

[/java]

The password is stored in plaintext inside the database. If the database is compromised at any time, then the attacker will be able to have direct access to the user accounts because he can log in using the plaintext username-password pair gathered from the database. The highlighted code above shows that the application stores the password in clear text inside database.

Patched Code:

[java]

<%@page import="java.util.Calendar"%>

<%@page import="java.text.SimpleDateFormat"%>

<%@page import="java.text.DateFormat"%>

<%@page import="java.math.BigInteger"%>

<%@page import="java.security.MessageDigest"%>

<%@page import="java.sql.*"%>

<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">

<title></title>

</head>

<body>

<%

String user=request.getParameter("user");

String pass=request.getParameter("pass");

DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");

Calendar cal = Calendar.getInstance();

String reg_time = (dateFormat.format(cal.getTime())).toString();

String original = pass+reg_time;

String s = pass+reg_time;

MessageDigest m = MessageDigest.getInstance("MD5");

m.update(s.getBytes(),0,s.length());

String calc_hash = new BigInteger(1,m.digest()).toString(16);

if(calc_hash.length()<32)

{

calc_hash = "0"+calc_hash;

}

Class.forName("com.mysql.jdbc.Driver");

Connection con=(Connection)DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "root" ,"");

PreparedStatement ps = (PreparedStatement) con.prepareStatement("insert into users_crypt values(?,?,?)");

ps.setString(1,user);

ps.setString(2,calc_hash);

ps.setString(3,reg_time);

int res = ps.executeUpdate();

if(res>0)

{

out.println("Register success");

}

else

{

out.println("Register failed");

}

%>

</body>

</html>

[/java]

The patched code for broken authentication shows that the password is stored in salted MD5 hash format. The salt used for every new registration is different (date and time of registration is used as salt). If the database is compromised, then the attacker has to find the clear text for the hashed passwords or it will be of no use. The highlighted code shows the generation of the salted hash value from password and date-time.

Broken Session Management

This vulnerability exists in a web application that does not properly implement session management. For example, when a user logs out from his/her account, the application redirects him/her to some other page (the login page, for instance) but the session is not invalidated. Another example can be that the session cookie for post- and pre-login is same. This comes under a session management flaw.

Vulnerable Code:

[java]

<%@page import="java.sql.*"%>

<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">

<title> </title>

</head>

<body>

<%

String user = request.getParameter("user");

String pass = request.getParameter("pass");

Class.forName("com.mysql.jdbc.Driver");

Connection con = (Connection) DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "root" , "");

PreparedStatement ps=(PreparedStatement) con.prepareStatement("select * from users where username=? and password=? limit 0,1");

ps.setString(1,user);

ps.setString(2,pass);

ResultSet rs=ps.executeQuery();

if(rs.next())

{

session.setAttribute("useracc", rs.getString("user"));

out.println("Login success");

}

else

{

out.println("Login failed");

}

%>

</body>

</html>

[/java]

The session cookie JSESSIONID in the above code remains the same for pre- and post-login, which is a session flaw and a session fixation can be utilized on this web application.

Patched Code:

[java]

<%@page import="java.sql.*"%>

<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">

<title> </title>

</head>

<body>

<%

String user = request.getParameter("user");

String pass = request.getParameter("pass");

Class.forName("com.mysql.jdbc.Driver");

Connection con = (Connection) DriverManager.getConnection("jdbc:mysql://localhost:3306/userdb", "root" , "");

PreparedStatement ps=(PreparedStatement) con.prepareStatement("select * from users where username=? and password=? limit 0,1");

ps.setString(1,user);

ps.setString(2,pass);

ResultSet rs=ps.executeQuery();

if(rs.next())

{

session.invalidate();

request.getSession(true);

session.setAttribute("useracc", rs.getString("user"));

out.println("Login success");

}

else

{

out.println("Login failed");

}

%>

</body>

</html>

[/java]

The patched code invalidates the current session if the login is successful and generates a new session. This changes the post-login session cookie value. Now a session fixation is not possible, because pre- and post-session cookies are different.

Cross-Site Scripting (XSS)

XSS or cross-site scripting is a vulnerability that allows an attacker to execute scripts or inject HTML code on a website's page using any of the three types of XSS (reflected, stored, and DOM).

  • Reflected XSS is executed by the browser but it is not stored on the page.
  • Stored XSS is executed by the browser and the XSS code stays on the page.
  • DOM XSS is executed by the browser but it is done by modifying the document object model of the script used in page.

Vulnerable Code:

[java]

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

<title>XSS Vulnerable</title>

</head>

<body>

<form action="xss-vuln.jsp" method="post">

Enter your name: <input type="text" name="name"><input type="submit">

</form>

<%

if(request.getMethod().equalsIgnoreCase("post"))

{

String name = request.getParameter("name");

if(!name.isEmpty())

{

out.println("<br>Hi "+name+". How are you?");

}

}

%>

</body>

</html>

[/java]

The vulnerability in the above code is that the user input is not filtered and is used as it is as a part of message to be displayed to user. Enter the string <b>apple</b> and see the below message on page:

Patched Code:

[java]

<%@page import="org.apache.commons.lang.StringEscapeUtils"%>

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

Patch

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

<title>XSS Patched</title>

</head>

<body>

<form action="xss-patch.jsp" method="post">

Enter your name: <input type="text" name="name"><input type="submit">

</form>

<%

if(request.getMethod().equalsIgnoreCase("post"))

{

String name = StringEscapeUtils.escapeHtml(request.getParameter("name"));

if(!name.isEmpty())

{

out.println("<br>Hi "+name+". How are you?");

}

}

%>

</body>

</html>

[/java]

The vulnerable code can be patched by escaping the user input before it is used any further. To do this we use the function escapeHtml() of the StringEscapeUtils class. It is available inside the JAR file commons-lang-2.4.jar.

Insecure Direct Object Reference

If a web application does not properly check whether a user is authorized to access a particular resource, then it falls into this category. We can say that the application doesn't verify that the user is authorized to access that particular resource. For example: A user who logged in to his bank account can somehow access another user's account details by incrementing or decrementing the account number then the web application is vulnerable to this vulnerability.

Vulnerable Code:

[java]

<%@page import="java.util.Enumeration"%>

<%@ page import="java.sql.*" %>

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

<title>Account Balance</title>

</head>

<body>

<%

int flag = 0;

Enumeration e = session.getAttributeNames();

while (e.hasMoreElements())

{

String name = (String) e.nextElement();

String value = session.getAttribute(name).toString();

if(name.equals("useracc") && !(value.isEmpty()))

{

flag = 1;

break;

}

}

if(flag == 1)

{

String accno = request.getParameter("accno");

Class.forName("com.mysql.jdbc.Driver");

Connection con = (Connection) DriverManager.getConnection("jdbc:mysql://localhost/mydb", "root", "");

PreparedStatement ps = (PreparedStatement) con.prepareStatement("select * from account_balance where accno=? limit 0,1");

ps.setString(1,accno);

ResultSet rs = ps.executeQuery();

if(rs.next())

{

String s = rs.getString("balance");

out.println("<h1>Welcome to your account</h1>");

out.println("<br>Account Number: "+session.getAttribute("useracc"));

out.println("<br>Your current balance is: "+s);

}

else

{

out.println("Error: Contact administrator.");

}

}

else

{

response.sendRedirect("login.jsp");

}

%>

</body>

</html>

[/java]

The vulnerability in the above code is in the line where "accno" stores the bank account number of the user, which it receives from a get parameter accno. Later, it is used as an input to fetch the bank balance from the database. By changing the value of "accno," anybody's bank details can be accessed.

Patched Code:

[java]

<%@page import="java.util.Enumeration"%>

<%@ page import="java.sql.*" %>

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

<title>Account Balance</title>

</head>

<body>

<%

int flag = 0;

Enumeration e = session.getAttributeNames();

while (e.hasMoreElements())

{

String name = (String) e.nextElement();

String value = session.getAttribute(name).toString();

if(name.equals("useracc") && !(value.isEmpty()))

{

flag = 1;

break;

}

}

if(flag == 1)

{

String sess_accno = session.getAttribute("useracc").toString();

String accno = request.getParameter("accno");

if(sess_accno.equals(accno))

{

Class.forName("com.mysql.jdbc.Driver");

Connection con = (Connection) DriverManager.getConnection("jdbc:mysql://localhost/mydb", "root", "");

PreparedStatement ps = (PreparedStatement) con.prepareStatement("select * from account_balance where accno=? limit 0,1");

ps.setString(1,accno);

/*

This line will be better

ps.setString(1,sess_accno);

*/

ResultSet rs = ps.executeQuery();

if(rs.next())

{

String s = rs.getString("balance");

out.println("<h1>Welcome to your account</h1>");

out.println("<br>Account Number: "+session.getAttribute("useracc"));

out.println("<br>Your current balance is: "+s);

}

else

{

out.println("Error: Contact administrator.");

}

}

else

{

out.println("Unauthorized Access Detected");

}

}

else

{

response.sendRedirect("login.jsp");

}

%>

</body>

</html>

[/java]

The patched code shown above checks whether the value of account number is the same as that of a session "useracc" (session is created at the time of login). If it matches, only then is further processing done, else an error message is displayed. Also, it would be better to use session value for the account number instead of the user-supplied value.

Security Misconfiguration

This happens when an application cannot handle unexpected user input that leads to an exception occurring inside the application. If an exception is not properly handled, it may contain sensitive information, such as parts of the source code where the exception occurred. To fix this issue, proper exception handling should be done to handle the exception and a custom error message should be shown in place.

Vulnerable Code:

[java]

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<%@ page import="java.sql.*" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

<title>Products</title>

</head>

<body>

<%

int id = Integer.parseInt(request.getParameter("id"));

Class.forName("com.mysql.jdbc.Driver");

Connection con = (Connection) DriverManager.getConnection("jdbc:mysql://localhost/mydb", "root", "");

PreparedStatement ps = (PreparedStatement) con.prepareStatement("select * from products where product_id=? limit 0,1");

ps.setInt(1,id);

ResultSet rs = ps.executeQuery();

if(rs.next())

{

out.println("<h1>Product Details</h1>");

out.println("<br>Product ID: "+rs.getInt("product_id"));

out.println("<br>Product Name: "+rs.getString("product_name"));

out.println("<br>Product Price: Rs. "+rs.getString("product_price"));

}

%>

</body>

</html>

[/java]

The vulnerability fails to handle any exception that might occur inside the application. As a result, the application shows an error message with information related to the application. Many times, snippets of the source code where exception has occurred are also included in the error message.

Patched Code:

[java]

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<%@ page import="java.sql.*" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

<title>Products</title>

</head>

<body>

<%

try

{

int id = Integer.parseInt(request.getParameter("id"));

Class.forName("com.mysql.jdbc.Driver");

Connection con = (Connection) DriverManager.getConnection("jdbc:mysql://localhost/mydb", "root", "");

PreparedStatement ps = (PreparedStatement) con.prepareStatement("select * from products where product_id=? limit 0,1");

ps.setInt(1,id);

ResultSet rs = ps.executeQuery();

if(rs.next())

{

out.println("<h1>Product Details</h1>");

out.println("<br>Product ID: "+rs.getInt("product_id"));

out.println("<br>Product Name: "+rs.getString("product_name"));

out.println("<br>Product Price: Rs. "+rs.getString("product_price"));

}

else

{

out.println("<h1>Product Details</h1>");

out.println("<br>Sorry, product not found.");

}

}

catch(NumberFormatException n)

{

out.println("Error: Invalid Product ID");

}

catch(Exception e)

{

out.println("Error: An error occurred");

}

%>

</body>

</html>

[/java]

Learn Secure Coding

Learn Secure Coding

Build your secure coding skills in C/C++, iOS, Java, .NET, Node.js, PHP and other languages.

The patched version of this code implements the exception handling of Java using try-catch. If any exception occurs inside the try block then no error message is shown. The exception handling code is defined in the catch block, which shows the error message for the exception that occurred.

Coder Geek
Coder Geek

Divya Mudgal a.k.a Coder Geek is an information security researcher and freelance application developer. A graduate in computer science, she has experience in secure coding, application development and researching the security side of application development.