27 January 2016

Cross Site Request Forgery (CSRF) is where an attacker tricks a user into executing malicious requests. Once you are authenticated into a web application any request coming from that browser will seem legitamate. Attackers can send users malicious links in emails, post them on forums, websites and even your own web application. The solution is to use a shared secret so that requests without the secret are ignored.

Liferay handles some of this for us through the use of authorization tokens. The auth tokens can be enabled by setting the propert “auth.token.check.enabled=true” in the portal-ext.properties file. What this does is place the “p_auth” and “p_p_auth” querystring parameters in Uniform Resource Locators (URL) when they are generated. Unfortunately this only protects URLs generated using portlet:actionURL or liferay-portlet:actionURL. It is up to us developers to protect against CSRF when it comes to the use of portlet:resourceURL or liferay-portlet:resourceURL.

The way to protect against CSRF is to have a shared secret between the client and the server. Liferay provides classes and objects we can use when using a resource URL. Liferay.authToken is a Javascript object set every page load requested from the Liferay server. AuthTokenUtil is a Java class defined in the Liferay Java API. Using them we will add a shared secret to our AJAX calls and validate the secret in our portlet server side code.

First let’s look at an AJAX call using JQuery. Notice the “p_auth” being added as data/parameter. The value for “p_auth” is assigned from the Javascript object Liferay.authToken.

Example AJAX call
$.ajax({
	url: '<portlet:resourceURL></portlet:resourceURL>',
	data: {'<portlet:namespace/>action': 'search',
		'<portlet:namespace/>q': searchVal,
		'p_auth': Liferay.authToken },
	cache: false,
	success: function (data) {
	  //do something  
	},
	error: function (x, t, m) {
	  //handle error
	}
});

The next step is to take the value assigned to “p_auth” and validated it server side. This is shown below in the use of AuthTokenUtil. It relies on throwing an exception to register an invalid authorization token. This means the code in a serveResource method needs to be in a try catch block with the AuthTokenUtil.checkCSRFToken method being the first call inside the block.

Clean by build id
try {
  AuthTokenUtil.checkCSRFToken(request, this.getClass().getName());
  /*
	Put your code here after the authorization token check.
	If an exception is thrown it will not be executed.
 */

} catch (PrincipalException pe) {
	_log.error("You are not authorized to access resource. Possible CSRF attack. "
			+ "UserId: " + PortalUtil.getUserId(resourceRequest));
	_log.error("Invalid CSRF token!  Token: " + ParamUtil.get(request, "p_auth", "none"), pe);
	resourceResponse.setContentType("text/html");
	resourceResponse.getWriter().write(getUnauthorizedMessage());
}

Less Is More ~ Older posts are available in the archive.