Page 1 of 1

[Tutorial] Hands off the core - the trouble of the devoted programmer

Posted: Mon Oct 29, 2007 4:00 pm
by nullbarriere
Hands off the core - the trouble of the devoted programmer

When one wants to alter the behavior of Joomla! as such - as opposed to add some behavior -
there often seems to be only one way. Changing some code here, changing there until it does what we want it to do. Of course we do our best in coding, MVC, OOP, obedience to the guidelines and everything. To make it sound right, we give it a cool name: It's a CORE HACK.

To have an example: I want my admin login to accept username and password by GET protocol.

Now I here it screaming back there, why would you want this? Well, when developing a site, much more when developing new extensions, I'm logging in and out all the time, say 30 times a day.

So what I did back in the mambo days, I made a link on my desktop and in my favourites folder like this:
http://localsite/administrator/index.ph ... ypass 
The login credentials where transmitted by means of the GET protocol. Old file 'globals.php' was putting everything in $_REQUEST, so this lead me straight to the controlpanel without logging in.

Now why is this not working nowadays?

We lookaround a little and find the following line in 'admin.login.php':

Code: Select all

$credentials['username']=JRequest::getVar('username', '', 'method', 'username');
$credentials['password']=JRequest::getVar('passwd', '', 'post', 'string', JREQUEST_ALLOWRAW);
Without having to look into the JRequest class we can tell that the password is taken from the POST variables explicitly, interestingly in opposition to the username.
How to find the right line to change?

Since everybody is encouraged to name functions and methods after the things they do ("talking identifiers") we look for the string 'login()' in all PHP files below the folder 'administrator'.

The CORE HACK would be easy, the password line can be altered to resemble the username line:

Code: Select all

$credentials['password'] = JRequest::getVar('password', '', 'method', 'password');
That’s it. Quick and dirty. To resolve some minor security problems :o we should make it configurable. We would need a switch to enable the GET protocol in your developing environment which would be your desktop or home LAN and to switch back to POST only on the server. Just 2 lines of code in the admin com_config component allow for this. This would be another CORE HACK I suppose.
The security issue

Accepting the login credentials from an URL makes it VERY easy and FAST for scripts to commit brute force or dictionary attacks.

Since this tutorial is about the devoted programmer and not the core hacker, you find the complete core hack including the config part in the attached file 'corehack_login_protocol.zip', optionally with the altered source ready for copying as whole files over your installation (J! 1.5 RC3).

Now I see you wondering back there, why don't you do just do it this way? Well, there will maybe or maybenot another RC before the final release, and even now people are thinking about Joomla! x.0, later on we'll have a security update.
When I started to modify Mambo in my early days I starte a sreadsheet table to keep track of my modifications, hoping it would enable an update. Instead this very table lead straight to this text - hands off the core. Nobody knows how many Mambo 4.x sites are still out there, just because they weren't updatable anymore after some CORE HACK.

Now we do it right.The best if not only way is to write a plugin.
Plugin?
Called mambots in ancient times plugins are pieces of code called at certain known places of the core code.

First the plugin has an parameter to choose wheter to allow only POST protocol as is the Joomla! default or accept GET passwords too if no POST password is given by login form.

Then the plugin needs to commit an easy task:

Just before the actual com_login is executed we check if
a) no POST password is set but
b) GET protocol is enabled by parameter and
c) GET password is present then
d) put GET password in POST request.

OK. Now let's do it.

Plugins come basically in four flavours: System, user, editor and content events, of which we choose the system group. In the system group there are as of the moment four documented core events: onAfterInitialise, onAfterRoute, onAfterDispatch, onAfterRender. As of which we choose onAfterRoute.
How to determine which event group/ event to use

Have a look at administrator/index.php. Find the line where the login component com_login would be called (terminus used is 'dispatched'). Look back a little to find the event triggered right befor this: triggerEvent('onAfterRoute');
Documentation at dev.joomla.org tells you the right group.

Now we build the plugin.

First we create an installation file 'login_protocol.xml' which contains all the meta info about autor, copyright, title, the actual plugin file and the parameter.

Here you go with the crucial parts, please refer to the manual or the developer wiki or the source code in the attached file 'plg_login_protocol.zip' for the complete installation file syntax:

Code: Select all

<filename plugin="login_protocol">login_protocol.php</filename>
and the parameter lines

Code: Select all

<param name="protocol" type="list" default="POST_only" label="Allowed Protocol" 
       description="Select in which protocol Joomla! should accept login credentials. 
       Prefer to use 'POST only' for public-access environments.">
	<option value="POST_only">Joomla! default: POST only</option>
	<option value="POST_or_GET">POST or GET</option>
</param>
Yes, that’s it.

The actual plugin file 'login_protocol.php' is not much more. It contains the plugin class 'plgSystemLogin_protocol' which in turn contains the sole event procedure 'onAfterRoute' which contains the above abcd) code:
if a) no POST password is set but b) GET protocol is enabled and c) GET pwd is present then d) put GET pwd in POST request.

Code: Select all

class plgSystemLogin_protocol extends Jplugin
{
	function onAfterRoute()
		{
		if(!JRequest::getVar('passwd', '', 'post', 'string', JREQUEST_ALLOWRAW))
			if($this->params->get('protocol','POST_only')=='POST_or_GET')
				if($passwordGET=JRequest::getVar('password', '', 'method', 'password'))
					JRequest::setVar('passwd', $passwordGET, 'post');
		}
	}
All we need to do to make this work is zipping 'login_protocol.xml' and 'login_protocol.php' into 'plg_login_protocol.zip' and install it in the backend.
! Don't forget to enable the plugin and set the parameter 'allowed protocols' to 'POST or GET'.

From now on you can reach your local Joomla! administration with a link similar to this:
http://localsite/administrator/index.ph ... ssword=pwd

! Please don't enable it out in the wild. Creatures like 'script kiddies' are waiting in the dark. That's what the parameter 'POST only' is meant for.