Problem/Motivation
When using domain language negotiation the destination parameter can be stripped.
Steps to reproduce:
- Install standard
- Install the language module
- Add second language
- Go to Configuration > Regional and language > Languages > Detection, click on Domain and make up some configuration and save it.
- Visit /admin/structure/block
- Click the seven link in local tasks
- Click remove on the first block
- Click click the remove button
- You should be on admin/structure/block/list/seven but you're on admin/structure/block
This can also cause errors with disabled javascript, domain language negotiation and Big Pipe enabled:
"Symfony\Component\HttpKernel\Exception\HttpException: The original location is missing. Drupal\big_pipe\Controller\BigPipeController->setNoJsCookie() (line 50 /core/modules/big_pipe/src/Controller/BigPipeController.php)"
Proposed resolution
Fix the \Drupal\Core\Security\RequestSanitizer to use \Drupal\Component\Utility\UrlHelper::externalIsLocal() to determine is an external URL is really external.
Remaining tasks
User interface changes
None
API changes
None
Data model changes
None
Release notes snippet
N/a
Comments
Comment #2
wim leersThat would be a bug in the Domain module then; it's stripping a query argument from a redirect URL when it shouldn't. Should be pretty easy to fix!
Comment #3
wim leersThis is the relevant code:
i.e. the
destinationURL query argument is being stripped.Comment #4
agentrickard@Wim
I'm not sure this is Domain module related. Core language negotiation by domain prefix is the likely issue here.
There is nothing in this very thin error report to indicate that Domain module is involved.
Needs more information from the original reporter.
Comment #5
agentrickard@Wim-
If this is Domain related, where is that code snippet from?
Comment #6
wim leersThe code snippet is from
\Drupal\big_pipe\Controller\BigPipeController::setNoJsCookie().It's totally possible the problem is not in Domain, but in core's language negotiation. What's certain is that something is stripping that query string. I read in the issue title and assumed the OP meant
domainmodule. I now see that it totally could've been domain-based language negotiation in core :) Sorry!Comment #7
agentrickardNo worries. But we still need more context from the original reporter.
Comment #9
alexpottI can reproduce the bug only with core. The problem is that core/lib/Drupal/Core/Security/RequestSanitizer.php is being too aggressive.
Here's how to reproduce:
This is being caused by \Drupal\Core\Security\RequestSanitizer::processParameterBag() stripping all external destinations. However all destination URLs will be external when domain language negotiation is configured.
Comment #10
alexpott#3018942: Domain URL language detection - InvalidArgumentException: The user-entered string must begin with a '/', '?', or '#' adds a test similar to #9 it'll need fixing when we fix this.
Comment #11
alexpottHere's a fix.
We're too early for
$GLOBALS['base_url']- that's done in \Drupal\Core\DrupalKernel::initializeRequestGlobals() which means we're not setting the base URL completely as expected but I think this check is good enough - if there's an insecure and malicious site available at the same domain then you've got more problems then redirects.Comment #13
alexpottFixed the tests and made the code a bit more robust if the Request object doesn't have all the information. Added a test for the case when the destination and the request are for the same domain.
Comment #14
alexpottImproved the issue summary and the comment in the patch.
Comment #15
krzysztof domańskiAfter adding new parameter $request we do not need parameter $bag ($bag = $request->$bag_name).
Comment #16
alexpott@Krzysztof Domański yep that looks good. Nice one.
Comment #18
AndyThornton commentedThe patch in #15 is working for me on Drupal 8.7.3 - thanks a lot.
Comment #19
borisson_This has sufficient testcoverage and I can't see anything at all that should change for this patch.
Comment #20
wim leersWow! Excellent investigative work in #9, @alexpott!
Comment #21
krzysztof domańskiFixed test failure #15.
Comment #22
alexpottThere needs to be a comment as to why catching the exception is required. Why at this point would either $destination or $request->getSchemeAndHttpHost() fail
Or is this being ultra defensive?
Let's pass in $request->getSchemeAndHttpHost() as an additional argument rather than changing the parameters.
Comment #23
alexpottLol I added the try catch in #13
So the answer is more defensive. I guess as this is security code that makes sense. So let's ignore #22.1
So once upon a time I thought the parameter change was a good idea too but looking at the loop that calls
processParameterBag()I'm now not so sure.Comment #26
krzysztof domańskiComment #28
nikitagupta commentedrerolled patch #21.
Comment #29
nikitagupta commentedComment #35
parisekComment #36
parisekcreated MR for 9.5
Comment #37
anothergasteizone commentedThe patch 2980527-29.patch did not work for 9.4.5 because classy has been replaced with stark. This patch should do the trick.
Comment #39
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It either no longer applies to Drupal core, or fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".
Apart from a re-roll or rebase, this issue may need more work to address feedback in the issue or MR comments. To progress an issue, incorporate this feedback as part of the process of updating the issue. This helps other contributors to know what is outstanding.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #40
raphaelbertrand commentedSimplier solution might be to change this line (61) in big_pipe.module,
method big_pipe_page_attachments to set destination parametter to local uri instead of absolute
by the way it will not be detected as external. The right host be already set by the route of big_pipe.nojs .
'content' => '0; URL=' . Url::fromRoute('big_pipe.nojs', [], ['query' => \Drupal::service('redirect.destination')->getAsArray()])->toString(),
Comment #41
nginex commented#37 worked for me, thanks
Comment #43
sukr_s commentedThis issue is resolved if solution in #3424701-30: Domain-based language negotiation should retain "destination" URL query argument is accepted.
Comment #44
smustgrave commentedThink this would be a good plan. Lets postpone this one for the fix in #3424701: Domain-based language negotiation should retain "destination" URL query argument then we can reopen this one for expanding test coverage and removing the todo in the code.
Comment #45
smustgrave commentedSo the fix in #44 needed to update the tests too. So going to close as duplicate and move over credit.
Comment #46
catchI just committed #3424720: LanguageNegotiationUrl unnecessarily adds domain to outbound URL's which fixes this issue. Credit wasn't transferred over originally, but we can assign credit for duplicate issues now, so doing that here.