CSRF Protection

Posted on November 4, 2012. Filed under: java/j2EE |


In the following example, Cross Site Request Forging (CSRF) is executed with a request tampering tool Tamper Data plugin.

ATTACK

The following servlet is vulnerable to CSRF attack.

Once hacker steals the victim’s JSESSIONID with Cross Site Scripting attack, hacker can then issue a request, tamper the JSESSIONID in the request header, then pose as the victim. There are many request tampering tools, for example, firefox Tamper Data plugin.

— antiCSRF.jsp —

<%@ 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”&gt;

<html>

<head>

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

<title>Insert title here</title>

</head>

<body>

<form action=”/test-app/antiCSRF” method=”post”>

Submit Bid Price:

<input type=”text” name=”price”><br>

<center>

<input type=”submit” value=”Submit Order”>

</center>

</form>

</body>

</html>

— antiCSRF.java —

package testPackage;

import java.io.IOException;

import java.io.PrintWriter;

import javax.servlet.ServletException;

import javax.servlet.annotation.WebServlet;

import javax.servlet.http.*;

import org.owasp.esapi.ESAPI;

@WebServlet(“/antiCSRF”)

public class antiCSRF extends HttpServlet {

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

response.setContentType(“text/html”);

PrintWriter out = response.getWriter();

String par1 = request.getParameter(“price”);

out.println

(“<html>\n” +

“<head>Test ESAPI Anti-CSRF demo</head>\n” +

“<body>\n” +

“<br><br>Bid price=” + ESAPI.encoder().encodeForHTML(par1)+

“<br><br>Cookie: “+request.getHeader(“Cookie”)+

“</body></html>”);

}

}

DEFENSE

The above CSRF attack can be fixed by including an anti-CSRF Token in each request.
We need to use an Utility class, CSRFTokenUtil.java
— CSRFTokenUtil.java —

package testPackage;

import java.security.SecureRandom;
import java.security.NoSuchAlgorithmException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.servlet.ServletException;

/**
* Based on OWASP CryptoUtil.java and CSRFGuard.java written by Eric Sheriden for OWASP
*
* This version by Rohit K. Sethi, Security Compass
* Created Aug. 9, 2007
*
* This is a very basic tool to add a unique token to form fields for
* mitigating against Cross Site Request Forgery (CSRF) attacks.
* http://code.google.com/p/owasp-esapi-java/issues/detail?id=162
*/
public final class CSRFTokenUtil
{
private final static String DEFAULT_PRNG = “SHA1PRNG”; //algorithm to generate key
public final static String SESSION_ATTR_KEY = “CSRF_TOKEN”;
private final static String NO_SESSION_ERROR = “No valid session found”;
/**
* Generates a random token to use in CSRF with the default
* Crytopgrahically strong Pseudo-Number Random Generator
*
* @return A string with a random token
* @throws NoSuchAlgorithmException if PRNG algorithm is not valid
*/
private static String getToken() throws NoSuchAlgorithmException
{
return getToken(DEFAULT_PRNG);
}

/**
* Generates a random token to use in CSRF with the default
* Crytopgrahically strong Pseudo-Number Random Generator
*
* @param prng Random Number generator to use
* @return A string with a random token
* @throws NoSuchAlgorithmException if PRNG algorithm is not valid
*/
private static String getToken(String prng) throws NoSuchAlgorithmException
{
SecureRandom sr = SecureRandom.getInstance(prng);
return “” + sr.nextLong();
}

/**
* Retrieves the CSRF token from the current session. Creates a token if one
* does not already exist
* @param session HTTP Session for user – must be valid
* @return token for the session, a new one is created if it doesn’t already exist
* @throws ServletException if session is null
* @throws NoSuchAlgorithmException if random number generator algorithm doesn’t exist
*/
public static String getToken (HttpSession session) throws ServletException, NoSuchAlgorithmException {
//throw exception if session is null
if (session == null) {
throw new ServletException(NO_SESSION_ERROR);
}

//Now attempt to retrieve existing token from session. If it doesn’t exist then
//add it
String token_val = (String)session.getAttribute(SESSION_ATTR_KEY);
if (token_val == null){
token_val = getToken();
session.setAttribute(SESSION_ATTR_KEY, token_val);

}
return token_val;

}

/**
* Tests whether or not the value of the CSRF_TOKEN parameter in the request
* is equal to the value of the CSRF_TOKEN attribute in the session
* @param request Inbound HttpRequest that you wish to check if it has a valid
* anti-CSRF token
* @return true if the parameter value matches the token in the session, false otherwise
* @throws ServletException If the session is null
* @throws NoSuchAlgorithmException if random number generator algorithm doesn’t exist
*/
public static boolean isValid (HttpServletRequest request)
throws ServletException, NoSuchAlgorithmException {
//throw exception if session is null
if (request.getSession(false)== null) {
throw new ServletException(NO_SESSION_ERROR);
}
System.out.println(getToken(request.getSession(false)));
System.out.println(request.getParameter(SESSION_ATTR_KEY));
return getToken(request.getSession(false)).equals(
request.getParameter(SESSION_ATTR_KEY));
}

}

In antiCSRF.java, we can check if the anti-CSRF Token stored in the Session matches the one in request parameter or not. If not match, an error message is generated.

— antiCSRF.java —

package testPackage;

import java.io.IOException;

import java.io.PrintWriter;

import java.security.NoSuchAlgorithmException;

import javax.servlet.ServletException;

import javax.servlet.annotation.WebServlet;

import javax.servlet.http.*;

import org.owasp.esapi.ESAPI;

@WebServlet(“/antiCSRF”)

public class antiCSRF extends HttpServlet {

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

response.setContentType(“text/html”);

try {

if(!CSRFTokenUtil.isValid(request)){

response.sendRedirect(“/test-app/error.html”);

}

} catch (NoSuchAlgorithmException e) {

e.printStackTrace();

}

PrintWriter out = response.getWriter();

String par1 = request.getParameter(“price”);

out.println(request.getParameter(CSRFTokenUtil.SESSION_ATTR_KEY));

out.println

(“<html>\n” +

“<head>Test ESAPI Anti-CSRF demo</head>\n” +

“<body>\n” +

“<br><br>Bid price=” + ESAPI.encoder().encodeForHTML(par1)+

“<br><br>Cookie: “+request.getHeader(“Cookie”)+

“</body></html>”);

}

}

we embed a text field which contains anti-CSRF Token in antiCSRF.jsp

— antiCSRF.jsp —

<%@ page language=”java” contentType=”text/html; charset=ISO-8859-1″

pageEncoding=”ISO-8859-1″%>

<%@ page import=”testPackage.CSRFTokenUtil”%>

<!DOCTYPE html PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN” “http://www.w3.org/TR/html4/loose.dtd”&gt;

<html>

<head>

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

<title>Insert title here</title>

</head>

<body>

<form action=”/test-app/antiCSRF” method=”post”>

<input  type=”text” name=<%=CSRFTokenUtil.SESSION_ATTR_KEY %> value=<%=CSRFTokenUtil.getToken(request.getSession(false)) %>>

Submit Bid Price:

<INPUT TYPE=”TEXT” NAME=”price”><BR>

<CENTER>

<INPUT TYPE=”SUBMIT” VALUE=”Submit Order”>

</CENTER>

</FORM>

</body>

</html>

Advertisements

Make a Comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Liked it here?
Why not try sites on the blogroll...

%d bloggers like this: