Problem/Motivation

This was originally reported to the security team but was cleared for a public issue.

DefaultExceptionHtmlSubscriber clones the original request when making a subrequest to display 401 error messages.

However a BadRequestException means the request is bad, and if code called on the error page (e.g. form building) throws another BadRequestException this can create an infinite loop.

Steps to reproduce

Proposed resolution

  • MR !10153: Get rid of the subrequest for 400 errors and just show a basic error page, or
  • MR !10306: Catch BadRequestException when building breadcrumbs

Remaining tasks

Review the two proposed resolutions.

User interface changes

Introduced terminology

API changes

Data model changes

Release notes snippet

Issue fork drupal-3486972

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

catch created an issue. See original summary.

catch’s picture

Status: Active » Needs review

Re-running a couple of functional test jobs due to random test failures but otherwise MR should be green now.

longwave’s picture

Status: Needs review » Needs work
catch’s picture

Status: Needs work » Needs review

Converted to a data provider.

catch’s picture

Title: DefaultExceptionHtmlSubscriber should not clone the request for 401s » DefaultExceptionHtmlSubscriber should not clone the request for 400/BadRequestException

Fixing issue title.

longwave’s picture

Status: Needs review » Needs work

Looks good, but we can lean on the data provider even more.

catch’s picture

Status: Needs work » Needs review

Makes sense, applied the suggestions.

mfb’s picture

One minor issue I noticed is excessive logging - three messages are logged for a bad request: "page not found" warning, "client error" warning, "php" error

bkosborne’s picture

After updating symfony/http-foundation to 6.4.14, I started experiencing out of memory errors for requests for paths like this:

/+/testing

With symfony/http-foundation 6.4.13, a request like that on my site would show the normal 404 page. But 6.4.14 has this in Request.php:

        if ('' !== $uri && (\ord($uri[0]) <= 32 || \ord($uri[-1]) <= 32)) {
            throw new BadRequestException('Invalid URI: A URI must not start nor end with ASCII control characters or spaces.');
        }

I guess the + is interpreted as a space so that exception is thrown. Then I get an infinite loop as described here and eventually an out of memory error.

However, with this patch, applied, while I no longer get an infinite redirect loop, I also don't get the normal 404 page anymore. Instead, I get a page that just has message No route found for "GET https://mysite.com/+/test"

Is that expected behavior with this patch? that the 404 page wouldn't be shown anymore for bad requests? I guess that makes sense.

catch’s picture

@bkosborne yes that's the expected behaviour with the MR.

There are other things we could do, like make a subrequest with a sanitized path or similar, but to me it feels both more complex and less robust compared the the un-themed error. What we end up with is similar to fast 404 handling which is done for similar reasons (e.g. we don't think humans will see these, only bots and maybe API clients).

longwave’s picture

Status: Needs review » Reviewed & tested by the community

I think this is fine. This does mean that some pages that were themed 404s before the Symfony security update will now become a different error, but the behaviour change is in Symfony; this just fixes a related problem that already existed but is perhaps more visible now - but as @catch says this shouldn't happen to end users in normal operation.

catch’s picture

Issue tags: +beta target

Tagging as beta target because it's exacerbated by the latest Symfony release.

bkosborne’s picture

Okay, thanks for explaining. Sounds good. I agree that requests seeing these errors will be bots.

  • larowlan committed 297bcc4d on 10.3.x
    Issue #3486972 by catch, longwave: DefaultExceptionHtmlSubscriber should...

  • larowlan committed a4dbd3f2 on 10.4.x
    Issue #3486972 by catch, longwave: DefaultExceptionHtmlSubscriber should...

  • larowlan committed 0675e266 on 10.5.x
    Issue #3486972 by catch, longwave: DefaultExceptionHtmlSubscriber should...

  • larowlan committed 2c571110 on 11.0.x
    Issue #3486972 by catch, longwave: DefaultExceptionHtmlSubscriber should...

  • larowlan committed 1f96131d on 11.1.x
    Issue #3486972 by catch, longwave: DefaultExceptionHtmlSubscriber should...

  • larowlan committed a54602de on 11.x
    Issue #3486972 by catch, longwave: DefaultExceptionHtmlSubscriber should...
larowlan’s picture

Version: 11.x-dev » 10.3.x-dev
Status: Reviewed & tested by the community » Fixed

Committed to 11.x and backported to 11.1.x, 11.0.x, 10.5.x, 10.4.x and 10.3.x as this is a critical bug and there's minimal risk of disruption.

  • larowlan committed 18fcfcb7 on 10.5.x
    Revert "Issue #3486972 by catch, longwave:...

  • larowlan committed b878dde6 on 10.4.x
    Revert "Issue #3486972 by catch, longwave:...

  • larowlan committed 573c7dca on 10.3.x
    Revert "Issue #3486972 by catch, longwave:...

  • larowlan committed 8247b90e on 11.1.x
    Revert "Issue #3486972 by catch, longwave:...

  • larowlan committed 5d700891 on 11.0.x
    Revert "Issue #3486972 by catch, longwave:...

  • larowlan committed ccbbb4e5 on 11.x
    Revert "Issue #3486972 by catch, longwave:...
larowlan’s picture

Version: 10.3.x-dev » 11.x-dev
Status: Fixed » Needs work

Reverted this one, the failure in MediaSourceOEmbedVideoTest is from this change

catch’s picture

Status: Needs work » Reviewed & tested by the community

Test needed to be updated for a different error message, just an expectation change. Moving back to RTBC since the fix is trivial.

mfb’s picture

This issue ended up in the release notes for Drupal 11.0.9 but presumably that's a typo, since it was reverted

longwave’s picture

@mfb the revert is also in the release notes which is confusing, will remove both.

mfb’s picture

oh somehow missed that? nevermind :p

mfb’s picture

Huh I couldn't reproduce the test failure on this MR? 🙃🤷

mfb’s picture

Status: Reviewed & tested by the community » Needs review

I'm working on an alternate fix for this: Catch the exception when calling Request::create() in core/modules/system/src/PathBasedBreadcrumbBuilder.php

mfb’s picture

Issue summary: View changes
catch’s picture

Status: Needs review » Reviewed & tested by the community

@mfb PathBaseBreadcrumbBuilder looks like a different issue to me, I have seen this without breadcrumbs being involved at all (i.e. in form processing), could you create a new issue for that?

Restoring the RTBC here.

mfb’s picture

Well, the root of the issue here is that Request::create() never threw exceptions previously, but now it may. So, dealing with these exceptions did seem like the correct fix to me - and resolves my infinite loop.

Of course there is more than one Request::create() call in core, but there actually are not that many, outside of tests

catch’s picture

Well, the root of the issue here is that Request::create() never threw exceptions previously, but now it may.

No this predates the recent Symfony security release, for example InputBag and ParameterBag can also throw a BadRequestException and those have been in Symfony for years now - any code throwing a BadRequestException in the course of page building can trigger this.

mfb’s picture

Are you saying you were seeing an infinite loop prior to the recent symfony update? For me that's not the case, this was a new bug report.

catch’s picture

@mfb not with just core, but yes.

mfb’s picture

Interesting! In that case I'd want to look at those problems, but I don't know specifically where you were seeing that.

I can only talk about the two brand new issues I've looked at related to the new Request::create() exception, where it totally makes sense (imho) to catch it where it's thrown because we can logically return FALSE or NULL - https://git.drupalcode.org/project/drupal/-/merge_requests/10306 and https://git.drupalcode.org/project/drupal/-/merge_requests/10307

If you don't accept https://git.drupalcode.org/project/drupal/-/merge_requests/10306 for this issue then I can open a child issue (and I realize it needs a test, but I haven't gotten there yet :)

alexpott’s picture

Version: 11.x-dev » 10.3.x-dev
Status: Reviewed & tested by the community » Fixed

@mfb can you open another issue for your work on the path breadcrumb builder. Thanks!

Committed and pushed cec8630b61c to 11.x and 1641c8904af to 11.1.x and 25d18f8735f to 11.0.x and 22176d99bd1 to 10.5.x and c8034c72fdd to 10.4.x and 9414efaca36 to 10.3.x. Thanks!

  • alexpott committed 9414efac on 10.3.x
    Issue #3486972 by catch, longwave, larowlan:...

  • alexpott committed c8034c72 on 10.4.x
    Issue #3486972 by catch, longwave, larowlan:...

  • alexpott committed 22176d99 on 10.5.x
    Issue #3486972 by catch, longwave, larowlan:...

  • alexpott committed 25d18f87 on 11.0.x
    Issue #3486972 by catch, longwave, larowlan:...

  • alexpott committed 1641c890 on 11.1.x
    Issue #3486972 by catch, longwave, larowlan:...

  • alexpott committed cec8630b on 11.x
    Issue #3486972 by catch, longwave, larowlan:...
hkirsman’s picture

This seems to solve issue I had with encdoded backslash https://www.drupal.org/project/drupal/issues/3492437
But did I miss some commit from here because I'm now getting white page with plain text "Invalid URI: A URI cannot contain a backslash." It used to be normal page not found before.

mfb’s picture

See also the child issue #3490710: Catch potential exception when calling Request::create() in PathBasedBreadcrumbBuilder which should resolve it (or if not, you have to look at the chained exception stacktraces to see what exception you're hitting)

catch’s picture

But did I miss some commit from here because I'm now getting white page with plain text "Invalid URI: A URI cannot contain a backslash." It used to be normal page not found before.

No that's the change here - if the exception is thrown, then it produces a minimal exception error without a themed page, because the themed page could also throw the same exception again, leading to an infinite loop.

#3490710: Catch potential exception when calling Request::create() in PathBasedBreadcrumbBuilder and possibly other issues will make things more fault tolerant so the exception doesn't get thrown at all if it can otherwise be safely handled - as @mfb points out above.

hkirsman’s picture

ok, thanks!

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

c. s. comfort’s picture

What is the appropriate way now to throw a BadRequestHttpException that returns a themed (user-friendly) error page? We encountered this recently in the course of one of our custom modules throwing this exception only to find that we now get a blank page, whereas before we could present a themed page (i.e., for the system.4xx route).