Joomla!’s temporary off-line mode is a very handy option to temporarily take your site down while performing maintenance —e.g. updating the Joomla! core or an extension— and is even suggested by the official documentation for the unfortunate time that your site has been compromised. However, is this really off-line, or are there any pitfalls you should be aware of?

As you might have guessed from the title, the off-line mode is not so off-line as you would think it is. As a matter of fact, the off-line mode is a magic trick, performed like any illusionist would have done. While your site works normally below the hood —component running and plugins executing— Joomla! performs a magic trick just before it sends the page to the browser, swapping the real page with the off-line system template. It is the very reason why you can type in http://www.joomla.org?tmpl=offline in your browser bar and see the official Joomla! site being apparently off-line when it’s not. Let’s see how Joomla! performs this class act of illusionist trick, the implications and a viable workaround for truly taking your site off-line in the unlikely case of an emergency.

Dissecting a magic trick

Joomla! is in fact a PHP framework with several “applications” installed out-of-the-box. The front-end part of your site is one of them. Each application inherits the abstract JApplication class. For the front-end application, the concrete implementation of JApplication is called JSite and it can be found in the includes/application.php file. The only reference to the off-line mode in there is in the render() method, where you see something interesting and disturbing: Joomla! checks if you have set your site in off-line mode just before it renders the template. If your site is off-line and it is trying to serve an HTML page and there is no user with back-end access  logged in, Joomla! will load the offline.php template file instead of the regular index.php. All right, hold on a moment. When does the render() method gets called? The answer lies in the main index.php file found in your site’s root. The call to $mainframe->render() ($mainframe being a global variable which contains the JSite object) is the second to last statement in that file. In other words Joomla! will execute all plugins, run the component and only deal with the off-line mode during the final step, just before it is ready to output the HTML page to the browser. The JSite::render() method also tells us that only the HTML mode is affected by the off-line mode. And this, my friends, may have chilling effects on your site...

The chilling effects

Think for a minute when and why you might be using the off-line mode. For starters, when you are building a website and don’t want the world to see just yet. In that case the off-line mode is fine, as it merely hides the user-viewable content from random web visitors. In that sense, the off-line mode works just fine. But this is not the only, or even the most frequently used, case.

Remember the link I gave you in the introduction of the article, mentioning what you should do when your site gets hacked? The official documentation is to use the off-line mode immediately after being hacked. BIG MISTAKE! Most compromised sites happen because a resourceful cracker found a SQLi vulnerability or a direct file upload loophole on your site.

In the case of an SQLi vulnerability, a cracker appends an SQL statement of his liking to a URL on your site, directly executing his malicious commands against your site’s database. The premise of this attack is that the vulnerable component parses the query parameters the cracker sends to it. Guess what? Even in off-line mode, you are still susceptible to SQLi attacks against a vulnerable component. Why? Because the component will execute, but you will just not see its output. You are off-line and you’re still being hacked. Checkmate.

In the case of a direct file upload, the cracker uploads one of the C99-variant “root” scripts somewhere on your site, making use of a vulnerable component. The root script executes outside Joomla!, so even if you take the site off-line the hacker will still be able to use it. Let’s say you try to wise up, find that script and delete it. Ha! You beat the cracker... not. When he discovers that his script is gone, he can try to perform the direct upload again. Since the vulnerable component keeps on executing, despite not being visible, he can still upload the root script and completely own your site. Two points for the cracker, desperation for the site owner.

The bottom line is this: The off-line mode can’t and won’t protect you against a cracker.

My Kung-Fu is stronger than yours

You should have known by now. For every problem that gets in my way, I find a solution, document it and share it with the rest of the world. There is a viable workaround to completely taking your site off-line, as long as you are running Apache or any other server which supports .htaccess files and mod_rewrite. This pretty much means that it will work on all servers except most IIS installations. The idea is that we want only ourselves to have access to the site, showing a static HTML page to other visitors and the cracker, effectively outsmarting him in his own game.

The first thing you need is your public IP address, i.e. the IP used by your Internet connection. This can be usually found in your router’s connection information page and it has the form of 123.123.123.123, that is four numbers separated by dots. Note it down. For this example I am going to use the fake IP address 123.123.123.123. You also need an HTML file to show to your visitors. You can create one with any capable tool, i.e. Kompozer. You can use CSS and images if you’d like. I’ll pretend that you have created a file named offline.html and placed it in your site’s root.

Next up, you’ll create a plain text file with the following contents and upload it to your site’s root as .htaccess, overwriting your old .htaccess:

RewriteEngine On 
RewriteBase /$rewrite_base 
RewriteCond %{REMOTE_HOST} !123.123.123.123
RewriteCond %{REQUEST_URI} !offline.html
RewriteCond %{REQUEST_URI} !(.png|.jpg|.gif|.jpeg|.bmp|.swf|.css|.js)$ 
RewriteRule (.*) offline.html [R=307,L]

As you can see, there are three things to change in this file:

  1. 123.123.123.123 is your IP address. All you have to do is to “escape” the dots as backslash-dot.
  2. offline.html is the name of your HTML file, with the dot “escaped” as backslash-dot.
  3. offline.html is the name of your HTML file. Do not escape the dot in this case!

Note: If your site throws a blank page or Internal Server Error 500 upon uploading the file, replace R=307 with R (without the =307 part). This is only required on Apache 2.0 and 1.3 hosts, as these old versions do not support the R=307 flag.

As soon as you apply this file, the following will happen. If a request comes from any IP address except your own, the visitor will be redirected to the offline.html page with a temporary redirection HTTP status code (this is what R=307 does and it’s there to keep search engines from freaking out when confronted with your off-line site). However, PNG, JPG, GIF, BMP, SWF, CSS and JS files will still be served, so that you can use such media types in the offline.html file without any problems.

You can test it very easily. When you try to connect to your site, you should see no difference. If you use a device connected to the Internet from a different router, e.g. a mobile device connected to the Internet over its 3G connection, you should see the contents of the offline.html page. Victory is yours! You can now fix a hacked site without risking the cracker undoing your fixes while you’re working.

Can this thing be automated, please?

Well, yes, thank you for asking! I am working on an “emergency off-line” feature for Admin Tools Core —yes, the free as in Free Beer version— which will automate all those steps with a single click. This is planned for the 1.0 stable version, due for release in November. Until then, take care and be safe.

Credits and kudos

I'd like to thank Brian Teeman for speculating that the off-line mode wasn't so off-line after all in a Skype chat a few weeks back. This led me to dissect the code and find a solution to a problem I previously ignored. Thanks, Brian!

No comments