Problem/Motivation
Multiple reports in the Drupal 8 bug bounty program have highlighted vulnerabilities due to the presence of code in core/vendor being web accessible. These include reflected XSS, but
Critical rating is suggested by mlhess of the Drupal Security Team
original Drupal 8 bug bounty reports:
https://tracker.bugcrowd.com/submissions/2e56617070c27990b61200a993a06fa...
https://tracker.bugcrowd.com/submissions/3524c3fb858c2fb70ad17c802e48405...
duplicate ones:
https://tracker.bugcrowd.com/submissions/deaa4854541f290c4439e64232b7647...
https://tracker.bugcrowd.com/submissions/90358a13b3ec7be29e698fdc587a065...
https://tracker.bugcrowd.com/submissions/85d9dc0fb995ad145dda4522c03aab2...
Proposed resolution
Deny access to vendor/
Remaining tasks
User interface changes
none
API changes
None
Original reporters include:
@FluxReiners (twitter)
mak0
fady_osman
| Comment | File | Size | Author |
|---|---|---|---|
| #64 | 57-64-interdiff.txt | 2.69 KB | alexpott |
| #64 | 2508591.64.patch | 4.06 KB | alexpott |
| #60 | 2508591.60.patch | 3.07 KB | timmillwood |
| #57 | 2508591.57.patch | 3.07 KB | alexpott |
| #57 | 44-57-interdiff.txt | 3.87 KB | alexpott |
Comments
Comment #1
pwolanin commentedComment #2
pwolanin commentedComment #3
pwolanin commentedComment #4
mile23+1 on this, as an understatement.
I suggested something similar in one of the Composer issues, and the push-back was that we want D8 to be installable in shared hosting, where the user can't change docroot.
Also: Boo that I'd need a login to bugcrowd to read those linked issues.
Comment #5
larowlan+1 to app, not docroot
app is standard in other php frameworks
Comment #6
chx commentedReally.
Beyond not adding crappy third party code to Drupal which I understand is too late now why don't we just htaccess deny that dir? (I think we had an issue for that or two)
Comment #7
timmillwoodI think I agree with this but it needs to be executed correctly, for example the way it's described now if you download the drupal zip file and extract to a folder named "drupal" you will have the the following structure:
Which I think seems a bit of a mess, applications like Laravel have a public folder, is it too late to adopt that approach?
Comment #8
dawehnerIn case we would do that, we should also document how people on shared hosting would be able / should be able to run drupal, given that they have less control over what is done.
Comment #9
dawehnerI think we should certainly follow the suggestion of #6 ... there is no usecase in allowing to access anything from vendor.
In case we one day use composer and we get a usecase, we can tell people to opt in manually in
.htaccessIf I understand the problem space enough, this would already solve all issues with vendor. Together with #8 it is probably the only thing left.
In case you control the service environment you also control how the code is build, so that you can implement whatever scheme you want to use.
Comment #10
wim leers+1 to #6/#8/#9, for the reasons cited, plus one more: the other proposed solution would be very disruptive.
So I wonder if there's a reason why #6/#8/#9 is insufficient.
Comment #11
fabianx commentedFor shared hosting we can provide an index.php at the root still:
- index.php
require __DIR__ . '/public/index.php';
- .htaccess
ONLY allow index.php
Then a more permissive .htaccess in public/ should be able to allow access to something like another front controller.
Comment #12
fabianx commentedHowever I also think we should probably just lock out vendor/ and other dirs.
Comment #13
plachThis may be a stupid suggestion, and I have no idea whether it's feasible on the d.o. infrastructure level, but I'm wondering whether it would make sense to have a packaged version of Drupal that's specifically targeted to shared-hosting environments and one that strictly mirrors the repository. This way we could fix this issue in a best-effort way for the former by implementing #6 and provide the full directory revamp only for the latter.
Comment #14
plachI was wondering too: I guess that, since
.htaccessfiles may not be available/enabled, this is not a 100% reliable solution.Comment #15
chx commentedWe always relied on .htaccess for security. Always. This is not new.
This move is way too big a disruption this far into beta.
Comment #16
plachComment #17
catchYes this looks like a straight bug/omission in our .htaccess.
I'd consider a directory move hardening, we should open a separate, major, issue for that.
Also like plach I'm not convinced we need to change the structure of the actual code base to support non-webroot even on shared hosting. index.php is already extremely light so handling that in packaging seems preferable.
Comment #18
chx commentedComment #19
timmillwood@chx thanks for providing the patch, but the issue I see is what if #1475510: Remove external dependencies from the core repo and let Composer manage the dependencies instead gets done. I know we can cross this bridge when we get to it but I was wondering if there is any way composer (run by the d.o packaging script) could add the .htaccess and web.config, maybe by running a post-install bash script?
Comment #20
catchComment #21
plachThe double packaging approach may also resolve many of the issues we have to make composer work properly with our codebase.
Comment #22
chx commented> I know we can cross this bridge when we get to it
This is a critical issue the linked one is a postponed normal. So yes, we will cross that bridge when we get there.
Comment #23
timmillwoodI already have a patch (that needs review) for getting
composer installto run when packaging #2315545: Install composer dependencies to D8 when packaging so we could look at building on that.note: whatever the packager is doing, testbot should do the same #1923582: Add ability for testbot to run 'composer install' during installation.
Comment #24
chx commentedUnfollowing.
Comment #25
dawehnerSo yeah we should treat security issues over other issues this is for sure, but on the other hand I think not comitting the vendor dir is a good thing,
as it for example makes it easier to update 3rd party code, in case they would have a security issue.
Don't recall me wrong, but I think adding new files into the vendor directory shouldn't be an actual issue, composer should still be able to write all the other files in there.
Should we add some form of test coverage for that? I think having test coverage for a critical security issue is not a bad idea in general :)
Comment #26
yched commentedWould #7 be that disruptive tough - It mostly means moving index.php down into a public/ folder ?
Also, regarding vendor being web accessible : a drupal site is not just the web head, it's the web head + drush + the site-specific drush commands + the drupal console + the site-specific console commands + maybe other custom scripts. All of this runs with a single code base (the "site codebase"), using a single set of libraries in a single vendor folder. So that vendor folder also contains libs for, say, console or drush commands, which are completely not intended for web use.
vendor being in the web docroot is indeed a view of the past :-)
Comment #27
sam152 commentedCan't we just put the rule to deny core/vendor into the DRUPAL_ROOT htaccess file? That way the immediate security issue is fixed and we'll be covered in the future if any progress is made towards getting vendor out of the repo. Am I missing something?
Comment #28
timmillwoodEven after the IRC abuse I am happy to RTBC @chx's patch and will work on how we can remove vendor from the codebase, and how we can the packager to add .htaccess and web.config.
Comment #29
sam152 commentedAdding a critical security fix to a packaging script won't cover people who cloned the repo? Is #27 not worth discussing? There are already rules in there to deny access to PHP files, why have two files?
Comment #30
timmillwoodThis is pretty much what the patch in #15 is doing.
However when vendor is removed from the repo we need another solution, and this solution needs to work wherever vendor is (I hope it's not core/vendor).
Comment #31
dawehner@yched
You are doing an excelent job in being AFK.
But yes I agree, the vendor directory is full of stuff, which just don't belong into the land of front controllers. Front controllers are owned by the application,
and they use libraries.
Good question, I was wondering that too but assumed there is an obvious reason why not do do so. I could imagine that its security hardening,
as people might use their own custom .htaccess file.
Ideally we should do both, as in the future, especially with issues @timmillwood is working on, this vendor directory might be not part of your custom checkout without the vendor dir preprared by Drupal itself.
Comment #32
benjy commented#27 solves exactly that. It works for the point in time when we do get vendor out of the repo and it works now. It also keeps an entirely generated folder "clean". Setting to NW, see what others think.
Also, the patch in #15 has trailing white space.
Comment #33
alexpottI agree with @dawehner, I can not see the harm in also excluding core/vendor and /vendor (let's future proof) in the root .htaccess and web.config files.
Unnecessary space at the end of these lines.
Comment #34
benjy commentedAnother good point, people often move .htaccess rules into global Apache config to disable AllowOverrides along with other things. Having everything in the one .htaccess file makes it much less likely people miss some important settings.
Comment #35
timmillwoodPatch also adding vendor to .htaccess and web.config files
Comment #36
timmillwoodComment #37
davidwbarratt commentedIf we are going to change the directory structure, I opt for #2385387: Permanently split Drupal and Drupal core into seperate repositories
Comment #38
dawehnerSo are we sure we want to deny everything and not allow CSS/JS?
Comment #39
pwolanin commentedI would rather see us enforce the same .htaccess we use for private files directly in vendor instead of further complicating the root .haccess.
We want to both deny from all and disallow PHP execution.
Comment #40
pwolanin commented@dawehner - CSS and JS files should be in core/assets - I don't see any in core/vendor.
Comment #41
benjy commentedTrailing white space
And here.
Plus we need a newline at the end of the files.
Good question, not sure we should be including CSS/JS from vendor? Can't see any css/js files that we'd be using currently so lets just updated when that becomes a problem?
Comment #42
alexpott@dawehner I pretty sure since vendor has no way to interacting with our CSS and JS library system. If it does then it's a module and it goes somewhere else.
@pwolanin I think we can address this complexity with tests. See Drupal\system\Tests\System\HtaccessTest
Also re #7 and creating a docroot in Drupal, I ponder how we're going to make a module's .css and .js are going to end up as accessible if we adopted it. We already added and removed assetic once during D8.
Comment #43
timmillwoodOnly CSS and JS in vendor is
Comment #44
timmillwoodfixes from #41
Comment #45
pwolanin commentedsee #39 this is the wrong approach I think
Comment #46
catchWe can't make vendor/ web server writable, so the .htaccess enforcing just won't work.
We could add a hook_requirements() warning that checks for the presence of the file and warns if it's not there, but that'd be an addition to the patch.
Overall if we have this in one place, I'd put it in the root .htaccess file - it's not impossible someone does rm -rf vendor && composer install which would nuke it.
Root .htaccess you're already required to keep it as is or take responsibility for not doing so.
Comment #47
timmillwood@catch: +1 (also I really want to get rid of the vendor dir)
Comment #48
pwolanin commentedCan we put the private files .htaccess file in place when we run composer install and verify that it's present?
I agree we don't want to make it actually writable.
We could also have rules in the root .htaccess, but seems good to make this defended in depth.
Comment #49
geerlingguy commentedWhatever the outcome, should this issue need a change record? Or at least something documenting whatever change happens, especially with a recommendation for what you should do if you use Nginx or some other non-Apache non-IIS webserver.
Comment #50
catch@pwolanin while the vendor directory is in core, the .htaccess in vendor/ will be too - would need a revert of this issue to remove it.
Otherwise composer install is going to be run locally or by the package manager, Drupal can't do anything while it's actually being run.
I'm completely happy to have rules in both .htaccess and core/vendor/.htaccess for this issue.
Comment #51
catchhttps://groups.google.com/forum/#!topic/composer-dev/WgkZZY2qeoQ relevant discussion, although the answer was 'no' on there so far.
Comment #52
mile23Yay we make the world safe for Apache!
What about everyone else?
I go back to the original recommendation: Harden the file structure so it has a
docroot/orapp/directory.Comment #53
alexpott@Mile23 Apache and IIS. Other web servers already have to replicate the logic that is in
.htaccessandweb.configto be safe. This patch does nothing to change that.I would love Drupal to ship with a separate docroot and which only contains index.php and the public files directory - but doing that is this issue is way out of scope for fixing the problem and is way out of scope for Drupal 8 because doing this is going to take a lot of effort and planning. It is much more than just moving some files around. We need to think about shared hosting, site installation, assets provided by modules and themes.
The patch still needs tests added to
Drupal\system\Tests\System\HtaccessTestComment #54
catch@Mile23, please open a new issue for that. It's an entirely new hardening proposal.
This actual bug is due to an omission from our existing security layer we've had for years. I'm only interested in fixing that here, and that's the only thing I consider release blocking. It's possibly to run Drupal outside of the web directory, but we don't need to make it mandatory.
It's not just about adding the new directory as Alex points out - we'd need to change asset handling and other things too (like the statistics.php logger).
Comment #55
David_Rothstein commentedSo the .htaccess blocking is obviously a good idea as a backup, but, um:
Shouldn't we be filing upstream patches to fix any XSS vulnerabilities? I guess that wouldn't happen here (it would have to happen in private for most cases) but just don't want to see that point lost in all the hubbub...
Comment #56
alexpott^(\..*|Entries.*|Repository|Root|Tag|Template|vendor)$does not prevent access to anything in vendor. It needs to be something like:\.(engine|inc|install|make|module|profile|po|sh|.*sql|theme|twig|tpl(\.php)?|xtmpl|yml)(~|\.sw[op]|\.bak|\.orig|\.save)?$|^(\..*|Entries.*|Repository|Root|Tag|Template|vendor\/.*|core\/vendor\/.*)$|^#.*#$|\.php(~|\.sw[op]|\.bak|\.orig\.save)$This protects both vendor/ and core/vendor/
Comment #57
alexpottSo #56 is completely wrong! FilesMatch is only about matching files. There is no way from root .htaccess to block access to this directory.
I've written a composer script to ensure the .htaccess and web.config files are present. This can be tested by running the following command from DRUPAL_ROOT/core:
since it is not protected by any
.phpprotection adding in the root .htaccess. And I don't think that this file will disappear.Comment #58
benjy commentedAh bummer, above I was thinking we'd use < Directory> but of course, you can't use that in htaccess. Do we just need someone to test the IIS rules here?
Comment #59
timmillwoodalexpott++ thanks for being awesome!
Will test and rtbc as soon as I can.
Looks like it will work wherever the vendor directory is, which is awesome, and will be added by packager once we get that running composer install!
Comment #60
timmillwoodI think we should use
post-autoload-dumpso that the ensureHtaccess script "occurs after the autoloader is dumped, either during install/update, or via the dump-autoload command."Other than that I've tested the patch and it looks awesome.
Comment #61
dawehner@timmillwood
Do you mind writing a test that ensures that those files are really not accessible?
Comment #62
alexpott@dawehner that's what this does.
Comment #63
pwolanin commentedThe one added to vendor should also disable PHP execution like the public/private files one, unless I'm missing it, I don't see it there. See:
https://api.drupal.org/api/drupal/includes%21file.inc/function/file_htac...
Comment #64
alexpott@pwolanin good point - we can just re-use FileStorage::htaccessLines() and in doing I found a bug in it!
Comment #65
timmillwoodComment #66
catchCommitted/pushed to 8.0.x, thanks!
Comment #68
geerlingguy commentedDoes this need a change record?
Comment #69
dawehnerI really hope noone relied on this behaviour.
Comment #70
pwolanin commentedComment #71
pwolanin commentedComment #73
geerlingguy commentedBtw, if you're running Nginx, here is the location directive you need to add to your server block to deny access to the root vendor directory:
Comment #74
Jaesin commentedYou could also block access to all files in vendor: