Since I am developing Joomla! extensions (environment Xampp for Windows, Eclipse, Git) I am always proceeding the same way. I clone a project to my desktop outside my web folder (e.g. C:\Sources\GitHub\joomla-cms) and establish symbolic links from my web folder to the cloned project (e.g. C:\xampp\htdocs\joomla-cms <==> C:\Sources\GitHub\joomla-cms).
This worked fine for me and with this method I had the possibility to have other clones (e.g. an extension like JoomGallery) in the same Joomla! installation by establishing similar links (working together with the help of git exclude) and there was no interference of the various clones. I am doing so for some years and I never detected any problems with it, until today. I cloned a project containing alternate layouts for an extension (JoomGallery) and proceeded the same way explained above. But the layout was never applied. I found out, that the problem was caused by the use of the symbolic links. The linked files were not detected by Joomla! because the use of realpath()
in JPath::find()
// Start looping through the path set
foreach ($paths as $path)
{
// Get the path to the file
$fullname = $path . '/' . $file;
// Is the path based on a stream?
if (strpos($path, '://') === false)
{
// Not a stream, so do a realpath() to avoid directory
// traversal attempts on the local file system.
// Needed for substr() later
$path = realpath($path);
$fullname = realpath($fullname);
}
/*
* The substr() check added to make sure that the realpath()
* results in a directory registered so that
* non-registered directories are not accessible via directory
* traversal attempts.
*/
if (file_exists($fullname) && substr($fullname, 0, strlen($path)) == $path )
{
return $fullname;
}
does prevent that. As a result of that the default layouts were applied (without giving any error message). For me, this is a bug or at least not the job of Joomla! to prevent the use of symbolic links (see Apache FollowSymLinks). Nearly 99% of my Joomla! installation is working perfect in a symbolic linked directory (as long JPath::find() is not involved).
What do you think?
From the comment block it would appear to exist for security purposes
I have to quote myself:
Nearly 99% of my Joomla! installation is working perfect in a symbolic linked directory (as long JPath::find() is not involved).
Labels |
Added:
?
|
Category | ⇒ | Libraries |
I believe the intention here is to prevent compromised code from accessing files outside of the web root. Any extension that accepts a path from user input would be vulnerable to this sort of compromise. This in turn could lead to cross-site hacks when the same user has more than one site under the same account.
I suppose we could add an option to override this behaviour, but having it there by default is intentional and IMO it is quite reasonable. Instead we should probably work on fixing the other 99% that allows symlinks.
I'm closing this based on the advice of the Security Strike Team above
Status | New | ⇒ | Closed |
Closed_Date | 0000-00-00 00:00:00 | ⇒ | 2015-06-23 14:24:25 |
Closed_By | ⇒ | wilsonge |
I am not an expert regarding to security things but I hope I may add some points to the statements above although this issue has been closed by @wilsonge due to advise I must have overlooked ;-) .
I believe the intention here is to prevent compromised code from accessing files outside of the web root.
While
JPath::find('C:/xampp/htdocs/joomla-cms/somefolder', 'SymLinkToFileOutsideWebRoot.php'));
returns false
calls like
JPath::find('C://xampp/htdocs/joomla-cms/somefolder', 'SymLinkToFileOutsideWebRoot.php'));
JPath::find('file://C://xampp/htdocs/joomla-cms/somefolder', 'SymLinkToFileOutsideWebRoot.php'));
just work fine for symbolic linked files pointing to files outside the web root by returning $fullname
.
Symbolic linked files pointing to files inside the web root
JPath::find('C:/xampp/htdocs/joomla-cms/somefolder', 'SymLinkToFileInsideWebRoot.php'));
do not work either.
Any extension that accepts a path from user input would be vulnerable to this sort of compromise. This in turn could lead to cross-site hacks when the same user has more than one site under the same account.
In my opinion JPath::find()
should not be misused to filter and check user inputs.
If you ever do this:
JPath::find('C://xampp/htdocs/joomla-cms/somefolder', 'SymLinkToFileOutsideWebRoot.php'));
you should bang your head on the wall, repeatedly, because you've screwed up. What you must do instead is:
$myPath = 'C://xampp/htdocs/joomla-cms/somefolder';
$path = JPath::clean($myPath); // $path is set to 'c:/xampp/htdocs/joomla-cms/somefolder'
$files = JPath::find($path, 'whatever.php'); // no more symlink issues, whee!
Of course this works as expected, i.e. you get protection against symlinks.
The directory argument to JPath::find is supposed to be already filtered by JPath::clean. Under normal operation these paths are constructed by the various JPATH_* constants, hardcoded strings and the name of the component (which is already passed through JFilterInput's "cmd" filter) which is why you don't see them filtered: they don't need to, they are constructed in a way that guaranteed there will be no something:// prefix. Therefore under normal operation you will NOT get a c://
or file://
prefix unless you screw up and pass unvalidated, unfiltered user data. That's why you MUST go through JPath::clean when you're dealing with directories computed from user input (not just a directory they've entered, but even a directory computed from constants, hardcoded strings and user input). JPath::clean will automatically convert c://
to c:/
which is the correct representation of a drive root under Windows.
As for this:
JPath::find('file://C://xampp/htdocs/joomla-cms/somefolder', 'SymLinkToFileOutsideWebRoot.php'));
Why are you using, or allowing your users to use, a file://
stream wrapper? Sure, you can definitely stab yourself to death with a fork knife but this doesn't mean that the fork knife is unsafe, it only means that you're suicidal I guess. It's pretty much the same thing here. Please don't commit suicide by file://
stream wrapper.
Thanks @nikosdion for your detailed explanations and for letting me look and feel like a fool.
For sure you are right regarding to the use of JPath::clean() and the use of effective input filtering and validating and I fully agree with you (rather than preventing symbolic links outside or at least symbolic links inside the web root in JPath::find()).
I do admit that my examples above are maybe not all suitable to show what I wanted to.
But, as I am for sure not yet as experienced than the Security Strike Team members I will accept for now that preventing symbolic links in JPath::find() is necessary for security reasons.
Don't feel like a fool. This is just a teachable moment. Trust me, all of us have them – I'd be a damn fool pretending I don't have them ever :D
I know this is an old thread but others might find it like I did and use it as a reference.
I had the same issue so i am using a core override of Path class and have used:
// Is the path based on a stream?
if (strpos($path, '://') === false)
{
// Not a stream, so do a realpath() to avoid directory
// traversal attempts on the local file system.
// Needed for substr() later
$path = realpath($path);
// Needed for substr() later (Modified to allow symbolic links to files)
if(is_link($fullname))
{
$fullname = rtrim(self::clean($fullname), '/\\');
} else {
$fullname = realpath($fullname);
}
}
From the comment block it would appear to exist for security purposes
This comment was created with the J!Tracker Application at issues.joomla.org/joomla-cms/7216.