WASHINGTON DC Joomla! cyber security company – Herndon, Virginia
A government client in Washington D.C. contacted us a few days ago with a problem they were encountering when trying to run an automated security auditing tool, called HP WebInspect on the development version of their websites after testing an update to Joomla! 2.5 from the 1.5 version. In fact, they installed a fresh copy of Joomla! 2.5 with no extra content, themes, or plugins aside from what comes in a standard, vanilla installation, and they were getting the same error. In its most basic form, the issue was that the tool was sending cURL requests to pages on their server, and one of those requests was receiving a 500 Internal Server Error on a certain URLs, but was expecting a 404 Not Found error.
Here’s a similar query string to the one they were trying to use in the cURL request:
?component=com_search&task=www.webinspect.hp.com&itemId=101
As you may know, a 500 Internal Server Error is generally a catch-all, and usually does not come with a very helpful error message. Such errors can be caused by a misconfigured server, unrecognized commands in a .htaccess, or application-specific issues. My first step was to determine which of those was causing this issue. Because the issue was presenting itself only on the installations of Joomla 2.5 and not all 404 Not Found pages were causing 500 Internal Server Error pages, it decided it must be application-specific.
Another thing I noticed was that one of the sites on which I attempted the cURL request was returning a slightly more helpful error message:
Invalid controller: name='www', format=''
This gave me at least a starting point. Not being intimately familiar with all of the inner workings of Joomla!, I had to do some research. I first noticed that the ‘www’ in the error message was present in the ‘task’ parameter in the query string. So, I did a quick search on Google and found a Stack Overflow answer that explained how the task parameter is used. Turns out, it takes a value in “dot notation” in the form of “controller.method”. So when combined with the component, it is used to determine which view to display. Notice that that allows for two parts to the task; one on each side of the period. Ours had four parts, which is incompatible. But for the sake of learning, let’s chop off the last two parts and say the task is www.websinpect. In that scenario, it would look for a method called webinspect() in the file: /components/com_search/controllers/www.php.
At first I puzzled over this, noting that it could be an issue with a required plugin not being installed. But then I remembered the actual issue: they are wanting a 404 error, which would not be returned if a required plugin did exist. So obviously, the specific content of the error could not be important to our issue now. They were being fed a purposely incorrect URL in hopes of receiving a 404 error.
Which brought me to the next step: find where the error is being raised. After a little bit of research, I found that Joomla has two ways to show an error: one is to raise an exception in the code, and the second is to use the JError::raise() static method. The former of the two, raising an exception, always returns a 500 error, whereas the latter allows you to specify the status code.
To figure out where the error was coming from, I first looked in several language files for the error string mentioned above and finally came away with a language key found in /language/en-GB/en-GB.lib_joomla.ini:
JLIB_APPLICATION_ERROR_INVALID_CONTROLLER
From there it was easy to find the error; I just did a Find in Files search on the Joomla package, which led me to /libraries/joomla/application/component/controller.php. The error is found on line 296, which is as follows:
throw new InvalidArgumentException(JText::sprintf('JLIB_APPLICATION_ERROR_INVALID_CONTROLLER', $type, $format));
As you’ll remember, raising an exception doesn’t allow us to specify a status code, meaning that this error will always cause a 500 Internal Service Error when the controller specified in the task parameter of the query string is not found. This is not ideal, especially for a production environment, where it makes more sense to display a 404 error because that is in fact what is happening: the page (controller) they are looking for cannot be found.
So the fix? Just use the other error-raising method I mentioned above:
JError::raiseError(404, JText::sprintf('JLIB_APPLICATION_ERROR_INVALID_CONTROLLER', $type, $format)); return null;
As you can see, we’re providing the same error string and variables, but are able to also specify a 404 status code, which will trigger the 404 Not Found page instead of the 500 Internal Server Error page. Problem solved. If you go to the URL that was returning a 500 error, you’ll now see your 404 page instead.