Securing your Joomla! site

Today I was reading Brian Teeman’s blog post “Help my Joomla web site has been hacked!!“. It outlines a pretty much scary story of site hacking. As we all know, security is a speed race. We have to upgrade our site software before a potential hacker gets wind of our outdated scripts, otherwise we run the risk of having our site compromised before we can react. What’s more, the compromise might be exploited to the attacker’s benefit long after we have upgraded the vulnerable site software, adding to our confusion. Quite a nightmare. But the hidden gem of this story, is a link back to Brian’s “Are you a Dork?” post. He implies that attackers can easily guess the version of our site’s extension, before breaking in our site. How can they? And what can we do to stop them? Read on for the full disclosure.

Edit May 2012: The information in this post is severely outdated. You can use my Master .htaccess or Admin Tools Professional by AkeebaBackup.com instead.

A little background

As you (should) all know, Joomla! extensions are installed in two places inside your site’s directory structure: the administrator directory’s (back-end) and the site’s root (front-end) components, modules, plugins and templates sub-directories. One of the lesser known facts is that each component, by definition, has an XML manifest file which is stored inside the administrator/components/com_mycomponent directory. Here is the problem: a. this file says everything about your extension, including version number b. it is always present in the same well known location c. has a known, extension-specific name d. it is most probably web-accessible. This is a design flaw of Joomla! – it could just store this information in the database and be done with it. It is not a flaw of any particular extension and all extensions are vulnerable to this problem.

Let’s give an example of the problem. Let’s say that you have installed JoomlaPack on your site, hosted at http://www.example.com. Enter the following URL to your browser: http://www.example.com/administrator/components/com_joomlapack/joomlapack.xml. With any luck you should see a screenfull of information including the name of the software, the creation date and the version. To make a bad case even worse, Google gives you the opportunity to find out which servers are vulnerable to this problem, as Brian pointed out in his blog post.

Are you feeling like a sitting duck already?

Edit 23 Oct 2009: A small inaccuracy on my part. Brian Teeman explicitly mentions that the hidden danger is direct access to the XML files in his presentation “Joomla Hidden Secrets“. This is a must-watch presentation for all Joomla! site administrators. He also proposes a solution, which cuts short all access to XML files. My solution is a bit more elaborate and also protects you from potential RFI attacks in mal-coded extensions’ PHP files. Do note that future Joomla! releases will have a new rule in its htaccess.txt file, disabled by default, which disallows access to extensions’ XML files. This patch was approved yesterday, October 22nd.

Countermeasures

Prevention is the best defense. Instead of waiting for script kiddies and more serious hackers to close in and start hammering their tools on your site, it’s a prudent idea to make it much harder for them to get to files visitors are not supposed to. Think of it like this: if you had a brick and mortar store, would you allow every random client come inside your storage area, take a look at your supplier’s invoices and probably try tampering with your safe deposit box, or would you lock the door to the storage area? Obvious answer, isn’t it? Let’s do the same with our site.

There are many approaches to locking down the door. One is using file and directory permissions, but IMHO this is a bit too hard and ineffective. You can always password protect the administrator directory (use your host’s Control Panel to do that). The problem with this approach is two-fold:

  • Logging to your site’s administrator section requires two logins: one for the web server level password protection, one entering your Joomla! credentials
  • Not all extensions have their manifest files located in the administrator directory of your site. For example, plugins, front-end modules and front-end templates have their files stored in plugins, modules and templates directories respectively. Password protecting these directories is like throwing yourself against a wall holding a sharp knife, as these directories are the very place where those extensions also store user-accessible files (e.g. CSS and images!) which you do not want to become inaccessible. You get the picture, right?

Most of the same concerns are valid if you decide to use the trick I blogged about a while ago, using a .htaccess file per directory to completely disallow access to it. Even though this trick works well for directories which have no visitor-reachable content (e.g. the tmp, cache, logs and libraries directories) it will not work as intended with the adminstrator, components, modules and other directories which are the home for both system files (incl. XML manifests and executable code) and visitor-reachable content, such as CSS and media files.

Quite thankfully, Apache and most other web servers – even IIS with a commercial plug-in – offer us the mod_rewrite alternative. The rest of the article describes a solution based on Apache’s mod_rewrite implementation, which is also 100% compatible with Lighttpd’s implementation.

Using mod_rewrite rules inside our site’s .htaccess we will accomplish the following goals:

  1. Allow access to the index.php, index2.php and index3.php inside the administrator directory
  2. Disallow access to anything else inside the administrator directory, except select media types (JS, CSS, web image formats, HTML)
  3. We’ll do the same for the front-end directories components, templates, plugins, modules
  4. Completely disallow access to other Joomla! system directories (cache, includes, language, libraries, logs, tmp), except for includes/js which contains the Joomla! system Javascript files
  5. Allow access to the XML-RPC directory if and only if the call ends up to index.php (this directory has no visitor accessible material)
  6. Optionally allow access to PHP files inside the protected directories, in case you have call-back scripts used by e-commerce systems’ payment gateways.

In order to do that, we have to add the following code inside our site’s .htaccess file, just right below the RewriteBase directive:

Edit 23 Oct 2009: My original rule set caused XML-RPC to stop working. This is now fixed. Also, accessing your backend as http://www.example.com/administrator doesn’t work. You have to use http://www.example.com/administrator/index.php instead. I am working on this, but I think line 2 in the script below has to be rewriten to read [code]administrator/index.php[/code] instead of [code]$1[/code] at the very end. It looks like it’s server-specific, so take this advice with a grain of salt.

Edit 29 Oct 2009: As I thought, the original rules on lines 2 & 3 were causing some problems. So, I replaced them with the correct ones. The original rules had [code]$1[/code] instead of [code]administrator/index.php[/code] which a. didn’t allow you to access the backend without tucking the [code]/index.php[/code] to the end of the administrator URL and b. caused some buttons onseveral sites to stop working, including JoomlaPack’s “Backup Now” button. Heh… talking about epic failure :)

Updated October 29th, 2009 EET
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
## Back-end protection
RewriteRule ^(administrator[/]?)$ administrator/index.php [L]
RewriteRule ^(administrator/index.htm[l]?)$ administrator/index.php [L]
RewriteRule ^(administrator/index.php)$ $1 [L]
RewriteRule ^(administrator/index[2-3].php)$ $1 [L]
RewriteRule ^(administrator/(components|modules|templates)/.*.(jpg|jpe[g,2]?|png|gif|bmp|css|js|swf|htm[l]?))$ $1 [L]
# RewriteRule ^administrator/.*.php(.*))$ $1 [L]
RewriteRule ^administrator/(.*)$ index.php [F,L]

## Explicitly allow access only to XML-RPC's xmlrpc/index.php or plain xmlrpc/ directory
RewriteRule ^xmlrpc[/]?$ xmlrpc/index.php [L]
RewriteRule ^xmlrpc/index.php$ xmlrpc/index.php [L]
RewriteRule ^xmlrpc/(.*)$ xmlrpc/index.php [F,L]

## Disallow front-end access for certain Joomla! system directories
RewriteRule ^(includes/js/.*)$ $1 [L]
RewriteRule ^(cache|includes|language|libraries|logs|tmp)/.*$ index.php [F,L]

## Allow limited access for certain Joomla! system directories with client-accessible content
RewriteRule ^((components|modules|plugins|templates)/.*.(jpg|jpe[g,2]?|png|gif|bmp|css|js|swf|htm[l]?))$ $1 [L]
# RewriteRule ^((components|modules|plugins|templates)/.*.php(.*))$ $1 [L]
RewriteRule ^((components|modules|plugins|templates)/.*index.php(.*))$ $1 [L]
RewriteRule ^(components|modules|plugins|templates)/.*$ index.php [F,L]

See the commented out lines (starting with a single hash)? These allow access to any PHP file inside the otherwise protected directories. This is dangerous! Remove them, unless you have an explicit need for callback PHP scripts running outside of Joomla!. Normally, this should not be necessary and even if it is, it is better to modify them in order to explicitly allow access to the specific PHP scripts to which you require direct access.

For this hefty mod_rewrite trick to work, you must make sure that your RewriteBase line is set up correctly. This is required if you have installed your Joomla! site in a subdirectory. For example, if your Joomla! site is located in http://www.example.com/portal, your RewriteBase line should be:

RewriteBase /portal

If you fail to do that, the protection will not work, as none of the rules will match the URLs making their way to the mod_rewrite Apache module.

Another thing requiring caution is the placement of these rules. As I’ve written above, they have to placed exactly after the RewriteBase directive in your .htaccess file. The only thing that may appear before them is perhaps a domain redirection (e.g. directing non www prefixed domain names to their www prefixed counterpart). More specifically, Joomla!’s anti-exploits and SEO rule stabs must appear after these site protection rules. The reason is that all rule sets contain “final” rules which stop mod_rewrite processing once they are triggered. Since Joomla!’s rules are very generic, putting my rule set below Joomla!’s would effectively cause my rule set to be always overridden.

I will be glad to receive your feedback in the comments below, as well as by email!

%d bloggers like this: