Java J2EE Security Considerations

Java Application Security Considerations

 1. Authentication

Insecure Coding Practices

Secure Coding Practices

Concatenated SQL queries for login validation.
In most cases it is seen that user credentials, as retrieved from the request is used to form concatenated login queries. Such instances result in injection flaws in the login page. In case of concatenated SQL query, it leads to SQL Injection vulnerability.

String username=request.getParameter(“username”);
String password=request.getParameter(“password”);


String query = “SELECT * FROM users WHERE username = “+username+” AND password=”+password;


Statement st = con. createStatement();
Results res = st.executeQuery(query);

Use Parameterized or pre-compiled queries.

String username=request.getParameter(“username”);
String password=request.getParameter(“password”);


String query = “SELECT * FROM users WHERE username =? AND password=?“;
PreparedStatement ps = con.prepareStatement(query);


ps.setString(1, username);
ps.setString(2, password);
Results res = ps.executeQuery();

No authentication check in internal pages / action
Generally applications does not implement authentication check for internal pages/action, which results in access to internal pages/actions without login/ a valid session.

Implement authentication code on all internal pages by making use of session variable

//After successful login. Set user identity in a session variable.
session.setAttribute(“username”, username);

//On every request, check if the user identity is present in the session.
String username = (String)session.getAttribute(“username”);
if(username == NULL)){
response.sendRedirect(“ErrorPage.java”);
return; }

Not restricting failed login attempts

If the application does not restrict invalid login attempts, it will allow a user to brute-force login and compromise other user accounts.

Track and restrict failed login attempts

Maintain a list of failed login attempts by a user.

 

Set a threshold limit of invalid attempts like 5.

 

Temporarily lockout the user on exceeding the threshold limit.  

 

  2. Authorization

Insecure Coding Practices

Secure Coding Practices

No data level access restriction.
Generally, applications expose primary keys or identifiers of data records as hidden fields to the users. And on request, use them to identify and process the requested records. However, as such inputs are trusted without any validation, they can be manipulated to access unauthorized data records from the system.

String query = “SELECT * FROM accts WHERE account = ?“;
PreparedStatement  pstmt = connection.prepareStatement(query , … );
pstmt.setString( 1, request.getParameter(“acct”));
ResultSet results = pstmt.executeQuery( );


Here, the logic above will fetch any record that matches the value of the request parameter – “acct“.

Implement data level access restriction.
Do not use un-trusted inputs as identifiers for data records. Apply data level access checks to ensure that the user has access to the requested record.
This is done using query constraint as shown below. Note that here the user identify is initially fetched from the user’s logged in session.

//Get User Identity in the session
String uname = (String) session.getAttribute(“username”);


//Fetch data record along with constraint (record validation)
String query = “SELECT * FROM accts WHERE account = ? and username = ?“;

PreparedStatement pstmt = connection.prepareStatement(query , … );
pstmt.setString( 1, request.getParameter(“acct”));
pstmt.setString( 2, uname);

ResultSet results = pstmt.executeQuery( );

No role based access restriction
Generally, applications hide the features from the users, based on their roles.  However, if role based access check is not implemented in the internal pages, a low privileged user will be able to access all the features meant
for high privileged users. 

Implement role based access restriction.
Apply role based access checks in all the features of the application. This check is to ensure that the user has right privileges to access the requested features.

This can be done by fetching the logged in user’s role from the database and verifying it against the roles required to access the requested feature.

//Get User Identify from the session.
String uname = (String) session.getAttribute(“username”);

 

//Fetching the user’s role from database
String query = “SELECT role FROM users WHERE username = ?”;

ResultSet results = pstmt.executeQuery( );
if(results. next()){  
String urole = results.getString(“role”); }


//Validating whether the user is allowed to access the feature

if (!urole.equals(“admin”)) {
response.sendRedirect(“Error.jsp”);

return;
}

3. SQL Injection

Insecure Coding Practices

Secure Coding Practices

Use of untrusted inputs (like the values fetched from request, database, session etc.) without any prior validation, to form concatenated SQL queries.


If the untrusted inputs are used to form concatenated SQL queries, it results in SQL Injection vulnerability.


//Untrusted Input
String customerID = request.getParameter(“customer”);

//Concatenated SQL Query
String query = “SELECT balance FROM customer_data where customer_id = “ + customerID;
Statement statement = connection.createStatement();
ResultSet results = statement.executeQuery(query);

ALWAYS use Precompiled or Parameterized SQL queries.
//Untrusted Input
String customerID = request.getParameter(“customerId”);

//Parameterized SQL Query
String query = “SELECT balance FROM customer_data where customer_id = ?“;
PreparedStatement ps = conn.prepareStatement(query);
ps.setString(1, customerID);
ResultSet results = ps.executeQuery();

 

4. Input Validation

Insecure Coding Practices

Secure Coding Practices

Use of untrusted inputs (like the values fetched from request, database, session etc.) without any prior validation, to form OS commands, file paths, URLs or stored in the database.


//Untrusted input
String name = request.getParameter(“req_name”);

//Saved in Database without any prior validation
String query = “INSERT into user_details VALUES (?);
PreparedStatement ps = con.prepareStatement(query);
ps.setString(1, name);

Validate all the untrusted inputs mainly request parameters, before processing them.
A) Whitelist validation – This refers to validating the user inputs based on their nature (like whether to accept alpha-numeric values or only numeric values etc.) These validations also include restriction for length and data type.
B) Blacklist validation – This refers to checking for the presence of special characters or certain set of backlisted characters in the input values.

//Untrusted input
String name = request.getParameter(“req_name”);

//Validate the input value before saving it in the Database.
Pattern p = Pattern.compile(“[^A-Za-z]”);
Matcher m = p.matcher(name);
boolean b = m.find();


if (b != true) {
String query = “INSERT into users VALUES (?);
PreparedStatement ps = con.prepareStatement(query);
ps.setString(1, name); }

6. Redirection Attack

Insecure Coding Practices

Secure Coding Practices

Use of untrusted inputs (like the values fetched from request, database, session etc.) without any prior validation, to form redirection URL value.
//Untrusted input
String url = request.getParameter(“redirect_to”);


//Used to redirect the user to the given URL.
response.redirect(url);

Use relative URLs for redirection. In case, redirection to external sites are required, restrict them to a set of specific domains.
A) Maintain a list of trusted domains
<%!
public static String m_TargetDomains[] = { “test.com”,
“example.com”, “google.com” }; %>


B) Validate the target URL present in the request by comparing it with the pre-defined list of trusted domains.  In case of no match, redirect the user to an Error Page.
<% String url = request.getParameter(“redirect_to”);
boolean validTarget = false;

for(int i = 0; i < m_TargetDomains.length; i++) {
if (url.equals(m_TargetDomains[i])) {
validTarget = true;
break; } }


if (validTarget == false ) {
response.sendRedirect(“Error.jsp”);
} else{ response.sendRedirect(url); }
%>

 

6. Cross-Site Scripting

Insecure Coding Practices

Secure Coding Practices

Displaying data in response without any encoding.
Applications, generally retrieve the user inputs from the request and process them without any validations. If such inputs values are displayed back to the user in any of the subsequent features, without any encoding, they result in Cross site scripting vulnerability.

//Untrusted input
<% String fname = request.getParameter(“fname”); %>


//Displaying value of the variable in the form element, without any validation or encoding
Fname:<input
type=”text” id=”fname” value=”<%= fname%>” />

Encode the data before displaying them in response
//Untrusted input
<% String fname = request.getParameter(“fname”);

//Encode the value to be displayed
String encodedName = HTMLencode(fname);

//Display the encoded value of the variable in the form element.
Fname: <input type=”text” id=”fname” value=”<%=encodedName %>” /> %>

7. Cross-Site Request Forgery

Insecure Coding Practices

Secure Coding Practices

Executing state changing requests without any token value
//A form that accepts user input. It does not contain any random token.


<html> <body>
<form name=”form1″ method=”get” action=”textInput.jsp”>
Enter your name:<input type=”text” name=”yourname” />
<input type=”submit” value=”Submit”/>
</form>

</body></html>


//On receiving the request, the form values are read and saved in the database. As there is no random parameter in the request, such requests can be easily forged from a valid user’s session.
<% String name=request.getParameter(“yourname“);
….
String query = “INSERT into user_details VALUES (?);
PreparedStatement ps = con.prepareStatement(query);
ps.setString(1, name);
….
%>

Implement Anti-CSRF token for state changing requests. Thus, making it difficult to forge such requests from other logged-in sessions.

A) Generate a random token for each authenticated user and save it in the user’s session.
session.setAttribute(“csrfToken”, generateCSRFToken());

private String generateCSRFToken() throws NoSuchAlgorithmException {
byte[] random = new byte[16];
BASE64Encoder encoder = new BASE64Encoder();
SecureRandom sr = SecureRandom.getInstance(“SHA1PRNG”);
sr.nextBytes(random);
return encoder.encode(random);
}

B) Add this security tokens to transaction pages, as hidden parameter.

String token = session.getAttribute(“csrfToken”);

<form name=”form1″ method=”get” action=”textInput.jsp”>
Enter your name:<input type=”text” name=”yourname” />
<input id=”csrftoken” type=”hidden” value=”<%=token%>” />
<input type=”submit” value=”Submit”/>
</form>

C) On receiving the request, validate it on the basis of this token. Accept the request only if the token is valid.

HttpSession session = request.getSession();
String storedToken = (String)session.getAttribute(“csrfToken“);
String token = request.getParameter(“csrftoken“);

//Validation check

if (storedToken.equals(token)) {
// process the request
} else { //reject the request }


//Invalidate sessions upon logout

session.invalidate();

8. Session Management

Insecure Coding Practices

Secure Coding Practices

Maintaining same Pre-login and Post-login session tokens
Usually applications maintain same session ID for pre-login and post-login pages. In all such cases, if the attacker obtains the pre-login session ID of any user, it can be easily used to hijack the post login session of that user.

Maintain different session tokens for pre-login and post-login sessions
//After successful login, invalidate current session and start a new session.
This will render a new Session ID to the user after login.
if (login succeeds) {
HttpSession userSession = req.getSession();
userSession.invalidate();
userSession = req.getSession(true);

…  }

No session invalidation after logout
If the application does not invalidate the current user session after logout, it will be easily hijacked. Access to internal pages of the application will be rendered, as the session of the user will remain active on the server.

Invalidate the session on logout

//On logout, invalidate the session
<%
HttpSession 
userSession = request.getSession();%>
<% 
userSession.invalidate();

No path and httpOnly cookie attributes set in the application
Generally application does not implement path and httpOnly cookie attributes. 

Not implementing the “httpOnly” attribute makes session ID, stored
in client side cookies readable to Javascripts.

Not implementing “path” attribute, makes browsers send the session
ID values even for other applications hosted on the same server.

Implement use of path and httpOnly cookie attributes

//Set additional attributes for session
cookie:
response.addHeader(“Set-Cookie”,”JSESSIONID=”+
id + “; Path=/<Application Root Directory Name>; HttpOnly”);

No session timeout set in the application
If the application doesn’t set session inactivity period, the sessions will remain idle on the server.

Maintain session timeout

//Configure
session timeout in web.xml file

<session-config>
<session-timeout>
15</session-timeout>
</session-config>

 

Comments

Popular posts from this blog

Apache Airflow Wait Between Tasks

Hibernate Interview Questions

Java Spring Interview Questions