Cross-site request forgery is a type of malicious exploit of a website where unauthorized commands are transmitted from a user that the website trusts. Joomla provides an internal mechanism to help prevent this exploit through the use of tokens associated with a user session.

What is cross-site request forgery?

Cross-site request forgery (also known as a one-click attack or session riding and abbreviated as CSRF or XSRF) is an attack that involves tricking a victim (this can be you, the Joomla super administrator, or one of your Joomla users) to send an HTTP request to a target destination (your Joomla website) without their knowledge or intent in order to perform an action as the victim.

For example, let's assume you are logged into your Joomla website as a super administrator. If you wanted to delete one of your content articles in the administrator interface, you would select the checkbox next to the article and click on the Trash button. Your browser is sending a POST request over HTTP to the following resource:

http://example.com/administrator/index.php?option=com_content&task=articles.trash&cid[]=1

In this situation, the request is intended and has been initiated by you.

However, now lets consider that you visit another website in a new tab of your browser. It may be an unknown website run by someone nasty, or it may be a website you trust, that has been hacked.

It would be very easy for a website to trick you into opening the above URL that removes one of your Joomla articles. This could be done through something subtle like a bogus image link or some javascript. Alternatively, it could be something obvious like a straightforward redirection.

Oh dear!

In this way, the type of CSRF vulnerability discussed above exploits the fact you are logged into your Joomla administrator, and tricks you into performing actions without your consent. The nature of the attack is that CSRF exploits the trust that a web site has for a user.

CSRF attacks can cause serious damage to a website. Lets consider just some of the actions which could be exploited:

  • Addition/Deletion of user accounts
  • Addition/Deletion of articles
  • Addition/Deletion of menus
  • Addition/Deletion of modules
  • Change of global configuration

Fortunately, this type of exploit is not possible in Joomla.

How does Joomla tackle CSRF?

Joomla prevents CSRF through the use of random session tokens.

The session tokens are random strings that are associated with the user's current session. These session tokens are then inserted within HTML forms and links associated with sensitive Joomla operations (such as the removal of articles or the creation of new users). When the user wishes to invoke these sensitive operations, the HTTP request will include this session token. It is then the responsibility of the appropriate Joomla controller to verify the existence and correctness of this token in the data that has been posted.

By including a session token with each request, Joomla can verify that the user actually intended to submit the desired requests. Inclusion of a required security token in HTTP requests associated with sensitive Joomla operations helps mitigate CSRF attacks as successful exploitation assumes the attacker knows the randomly generated token for the target victim's session. This is analogous to the attacker being able to guess the target victim's session identifier.

If no session token exists, or an incorrect token is posted, then Joomla can deny the request.

The most recent request was denied because it contained an invalid security token

You will probably have encountered this yourself at some time using your Joomla website. If you try to log into a Joomla site, or perform some other action such as saving an article, on a page that has stood idle for some time: it will fail with an 'invalid token' message.

The most recent request was denied because it contained an invalid security token. Please refresh the page and try again.

Third party extensions

The core Joomla package validates sensitive requests using session tokens, which mitigates the potential of CSRF attacks. However, what about third party extensions? Third party developers are responsible for implementing session tokens in their own code.

Using session tokens in your Joomla extensions

Adding and validating tokens to prevent CSRF is really easy using Joomla. There are really two situations where you will need to do this.

Firstly, when posting data in a form. Examples of this include:

  • Logging into the administrator
  • Creating a new article
  • Deleting articles from the article manager (yes, this is a form submission too)

In this case, we are submitting data to the server and the query strings (name/value pairs) are sent in the HTTP message body of a POST request.

And secondly, when creating a link to perform some action. An example of this may be a logout button.

In this case, we are requesting data from a server and query strings (name/value pairs) are sent in the URL of a GET request.

Using tokens when posting form data

If you are creating a page which includes a form that submits data to perform some action (for example, if your component is a shop, then it may be a form to create a new product), then you need to add your session token as an input into the form. You will not want the user to see this data, so you can add it as a hidden input. Adding a hidden input that contains the user's session token into a form is very simple, and can be achieved with this code:

<?php echo JHtml::_( 'form.token' ); ?>

The above code will output something similar to the following:

<input type="hidden" name="1234567890abcdef1234567890abcdef" value="1" />

An example of the usage of this in a form tag would be:

<form action="<?php echo JRoute::_(JUri::getInstance()->toString()); ?>" method="post" class="form-inline">
        <!-- Rest of form goes here -->
        <input type="hidden" name="option" value="com_buzz" />
        <input type="hidden" name="task" value="buzz.action" />
        <?php echo JHtml::_('form.token'); ?>
</form>

When the form is submitted, it will be posted to the buzz controller of the com_buzz component. If you are unfamiliar with the MVC structure that is implemented by Joomla you can lean more in our tutorials. However, what is important to understand is that in this example, the form data is submitted using the POST HTTP method to a method called "action" in a controller class in the following file:

/components/com_buzz/controllers/buzz.php

In that method, the first thing we want to check is that a valid session token has been submitted. If no valid session token is found in the form data, then we stop the execution of the script.

We check for a session token in the POST data using:

JSession::checkToken('post') or jexit(JText::_('JINVALID_TOKEN'));

So our action method may look like.

/**
 * Method to do some action.
 *
 * @access  public
 * @param   object   $model  The model.
 * @return  boolean  True on success, false on failure
 */
 public function action($model = null)
 {
        JSession::checkToken('post') or jexit(JText::_('JINVALID_TOKEN'));

        // Continue with controller routing
 }

Note, we passed a parameter 'post' to the checkToken method. This specifies what request method should be searched when looking for the session token (possible options are 'post', 'get' and 'request'). The default value for this parameter is 'post' meaning that we didn't really need to pass any parameter in this case.

Using tokens when creating links

If you want to create a link which points directly to a controller you can do this and still include a session token, which is appended to the URL. Using the same example above, we would achieve this using:

<?php
echo JRoute::_( 'index.php?option=com_buzz&controller=buzz.action&another=value&'. JSession::getFormToken() .'=1' );
?>

If you wanted to create a link in your page, you'd use this:

<a href="/<?php echo JRoute::_( 'index.php?option=com_buzz&controller=buzz.action&another=value&'. JSession::getFormToken() .'=1' ); ?>" />

Using this technique will add your session token directly to the URL. The output would look something like this:

<a href="/component/buzz/?controller=buzz.action&another=value&1234567890abcdef1234567890abcdef=1" />

When a user clicks on the link, they will be sent to the buzz controller of the com_buzz component. If you are unfamiliar with the MVC structure that is implemented by Joomla you can lean more in our tutorials. However, what is important to understand is that in this example, the users requests data using the GET HTTP method from a method called "action" in a controller class in the following file:

/components/com_buzz/controllers/buzz.php

In that method, the first thing we want to check is that a valid session token has been submitted. If no valid session token is found in the form data, then we stop the execution of the script.

We check for a session token in the GET data using:

JSession::checkToken('get') or jexit(JText::_('JINVALID_TOKEN'));

So our action method may look like.

/**
 * Method to do some action.
 *
 * @access  public
 * @param   object   $model  The model.
 * @return  boolean  True on success, false on failure
 */
 public function action($model = null)
 {
        JSession::checkToken('get') or jexit(JText::_('JINVALID_TOKEN'));

        // Continue with controller routing
 }

Note, we passed a parameter 'get' to the checkToken method. This specifies what request method should be searched when looking for the session token (possible options are 'post', 'get' and 'request'). The default value for this parameter is 'post'. If you are going to be using both the POST and GET methods then you can pass the parameter 'request' instead, which will catch values in both methods. It is better to try and limit the check to only one HTTP request method, so avoid using 'request' if possible.

Want the full article and instant website access?

Subscribe to JoomlaBuzz.com to discover and learn new Joomla skills.

Subscribe