Whether or not we like to think about it, there is always the potential threat of an attacker gaining access to our Joomla! websites. The most common way in which security is breached in Joomla! is through third-party extension security flaws.
Due to the number of extensions that have security defects, there is an official list of extensions that are considered insecure, available in the FAQ sections at http://help.joomla.org.
It is very important that, as third-party extension developers, we take great care in making our extensions as secure as we can.
How to Avoid Common Attacks
The security flaws that we will investigate are some of the most likely to be exploited because they tend to be the easiest to initiate and there is plenty of literature explaining how to initiate them.
The attack types described here should not be considered a complete list. There are many ways in which an attacker can attempt to exploit a system. If you are concerned about attacks, you should consider hiring a security professional to help evaluate security vulnerabilities in your extensions.
Using the Session Token
A session is created for every client that makes a request. Joomla! uses its own implementation of sessions; integral to this is the JSession class. The session token, also refered to as the ‘token’, is a random alphanumeric string that we can use to validate requests made by a client. The token can change during a session.
Imagine that an attacker uses a utility to bombard a site with data; the data itself may not be suspicious. The attacker may just be attempting to fill your database with worthless information. If we include a hidden field in our forms with the name of the token, we can check if the user is submitting data via a form with a valid session.
We can get the token using JUtility::getToken(). In our template, where we render the form we want to secure, we add this:
<input type="hidden" name="<?php echo JUtility::getToken();
?>" value="1" />
When we call JUtility::getToken() we can optionally provide the Boolean forceNew parameter. This will force the generation of a new token. Before doing this we must consider the context in which we are calling the method. If there are any other forms present on the page that also use the token we may inadvertently prevent these from working. Components are always rendered first so are generally safer when forcing a new token.
Now all we need to do is verify the token when we receive a request from the form that we are trying to secure. In this example we specifically get the token from the $_POST hash, guaranteeing that the token came via the correct method. The error message is not very intuitive; this is purposeful, because it makes it harder for an attacker to determine the reason why they are receiving the error.
if(!JRequest::getVar(JUtility::getToken(), false, 'POST'))
JError::raiseError('403', JText::_('Request Forbidden'));
Code injection occurs when code is included in input. The injected code, if not properly sanitized, may end up being executed on a server or on a client. There are a number of different ways in which injected code can compromise a Joomla! installation or a system with which we are interacting.
We will take a look at the two most common forms of code injection used to attack Joomla!: PHP and SQL code injection.
PHP Code Injection
We should use JRequest and, in some cases, REs to ensure that the input data that we are handling is valid. Most data validation is very simple and doesn’t require much effort.
Even when data comes from an XHTML form control that is restricted to specific values, we must still validate the data.
There is one form of PHP code injection that we don’t need to worry about. By default Joomla! always disables ‘register globals’. In scripts where ‘register globals’ is enabled, all URI query values are automatically converted into variables, literally injecting variables into a script.
Imagine we are using an input value to determine which class to instantiate. If we do not sanitize the incoming data, we run the risk of instantiating a class that could be used to malicious effect. To overcome this we could use a predefined list of class names to ensure the data is valid:
// define allowed classes
$allow = array('Monkey', 'Elephant', 'Lion');
// get the class name
$class = JRequest::getWord('class', 'Monkey', 'GET');
$class = ucfirst(strtolower($class));
Notice that we use the getWord() method to retrieve the value; this ensures that the value only includes letters and underscores. We also modify the case of the value so as to ensure it is in the same format as the expected value. Once we have defined the expectable class names and retrieved the value we can validate the value:
// unknown class, use default
$class = 'Monkey';
Imagine we want to execute a shell command. This type of process is potentially very risky; some unwanted malicious commands such as rm or del could potentially reduce our server to a gibbering wreck. In this example we define an array of acceptable commands and use the PHP escapeshellarg() function to escape any arguments passed to the command.
$allowCmds = array('mysqld', 'apachectl');
$cmd = JRequest::getVar('cmd', false, 'GET', 'WORD');
$arg = JRequest::getVar('arg', false, 'GET', 'WORD');
if( $cmd !== false && !in_array($cmd, $allow) )
$cmd .= ' '.escapeshellarg( $arg );
system( $cmd );
Using the correct escape mechanism for the system we are accessing is imperative in preventing code injection attacks.
Probably one of the most publicized vulnerabilities in PHP applications, SQL injection is potentially fatal. It is caused by inadequate processing of data before database queries are executed.
Joomla! provides us with the JDatabase methods getEscaped() and Quote() specifically for avoiding SQL injection. Consider the following value a’ OR name IS NOT NULL OR name=’b. If we used this value without escaping the value, we could inadvertently give an attacker access to all the records in a table:
SELECT * FROM `#__test` WHERE `name`='a' OR name IS NOT NULL OR
We can overcome this using the Quote() method:
$db =& JFactory::getDBO();
$name = $db->QuotegetEscaped(JRequest('name'));
Using the getEscaped() method escapes any special characters in the passed string. In our example the inverted comas will be escaped by prefixing them with a backslash. Our query now becomes:
SELECT * FROM `#__test` WHERE `name`='a' OR name IS NOT NULL OR
The Quote() method is identical to the getEscaped() method except that it also adds quotation marks around the value. Generally we should use Quote() in preference to getEscaped(), because this method guarantees that we are using the correct quotation marks for the database server that is being used.
Something else that we can verify is the number of results returned after we submit a query. For example, if we know that we should only get one record from a query, we can easily verify this.
$row = $db->loadAssoc();
if( $db->getNumRows() !== 1 )
// handle unexpected query result
XSS (Cross Site Scripting)
When we use JRequest::getVar() we automatically strip out XSS code, unless we use the JREQUEST_ALLOWRAW mask. We generally use this mask when dealing with large text fields that use are rendered using an editor; if we do not, valuable XHTML formatting data will be lost.
When we use the JREQUEST_ALLOWRAW mask we need to think carefully about how we process the data. When rendering the data remember to use the PHP htmlspecialchars() function or the static JOutput class to make the data safe for rendering in an XHTML page. When using the data with the database, remember to escape the data using the database object’s Quote() method.
If you want to allow your users to submit formatted data, you may want to consider using BBCode (Bulletin Board Code). BBCode is a simple markup language that uses a similar format to XHTML. Commonly used on forums, the language allows us to give the user the power to format their data without the worry of XSS. There are all sorts of BBCode tags; exactly how they are rendered may differ.
<img src=”/somewhere/smile.jpg” />
<div class=”quote”>Some quote</div>
Joomla! does not include any BBCode-parsing libraries. Instead we must either build our own parser or include an existing library. One such BBCode library is a class available from http://www.phpclasses.org/browse/package/951.html created by Leif K-Brooks and released under the PHP License. This class gives us lots of control; it allows us to define our own BBCode tags, use HTML entity encoded data, and import and export settings.
When we use BBCode, or a similar parsing mechanism, it is important that if we intend to allow the data to be editable, we store the data in its RAW state.
File System Snooping
A common error when working with files is to allow traversal of the file system. Joomla! provides us with a number of classes for dealing with the file system. This example imports the joomla.filesystem library and builds a path based on the value of the CGI request file (the path must not be relative).
$path = JPATH_COMPONENT.DS.'files'.DS
.JRequest('file', 'somefile.php', 'GET', 'WORD');
When we use the JPath::check() method, if $path is considered to be snooping, an error will be raised and the application will be terminated. Snooping paths are identified as paths that do not start with JPATH_BASE and do not attempt to traverse the tree using the parent directory indicator .. (two periods).
Other classes in the joomla.filesystem library include JFile, JFolder, and JArchive. It’s important to realize that none of these classes validate path parameters to prevent snooping. This is because there are times when we expect a path to be classified as snooping.