Problem/Motivation

If a CORS configuration is enabled in services.yml, it will break the submission of forms via the Drupal website's core UI (for example, updating permissions and updating a node via the edit form both fail), unless the site's own host name is specified as an allowed origin in the CORS configuration.

This CORS configuration leads to form submission failures:

  cors.config:
    enabled: true
    allowedHeaders: ['Content-Type', 'Access-Control-Allow-Headers', 'Authorization', 'X-Requested-With', 'X-CSRF-Token']
    allowedMethods: ['POST', 'GET', 'OPTIONS', 'PATCH', 'DELETE']
    allowedOrigins: ['http://localhost:4200']
    exposedHeaders: true
    maxAge: false
    supportsCredentials: true

Specifying allowedOrigins: '*' or allowedOrigins: ['http://localhost:4200', 'http://the-drupal-site.com'] works around the failure.

Proposed resolution

- Get the Pull Request that has been outstanding in the upstream library committed: https://github.com/asm89/stack-cors/pull/11
- Update the version requirement for that library in core/composer.json

Remaining tasks

- Tests

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

hampercm created an issue. See original summary.

Wim Leers’s picture

Hm, is this actually a bug? I guess we could whitelist all trusted hosts automatically (\Symfony\Component\HttpFoundation\Request::getTrustedHosts()).

This almost seems more like a documentation problem?

hampercm’s picture

CORS is only supposed to be relevant to cross-origin requests, hence the name, so it seems like a bad idea to maintain the current behavior. it will also encourage people to just specify allowedOrigins: '*’, which is less secure.

Wim Leers’s picture

Title: CORS breaks form submission unless allowed origins includes site's own host » [upstream] CORS breaks form submission unless allowed origins includes site's own host
Issue tags: +Needs upstream bugfix

CORS is only supposed to be relevant to cross-origin requests, hence the name, so it seems like a bad idea to maintain the current behavior.

That is a very good point! :)

However, I don't understand how this is possible, because \Asm89\Stack\Cors::handle() does this:

        if ( ! $this->cors->isCorsRequest($request)) {
            return $this->app->handle($request, $type, $catch);
        }

… and isCorsRequest() does this:

    public function isCorsRequest(Request $request)
    {
        return $request->headers->has('Origin');
    }

Aha!

I just submitted a form with Chrome, and sure enough, there was an Origin request header.

So guess, what? This has been broken since July 2014: https://github.com/asm89/stack-cors/issues/10 + https://github.com/asm89/stack-cors/pull/11.

Wim Leers’s picture

Issue tags: +CORS
piyuesh23’s picture

Just in case someone is looking for using the github patches, you can update your composer.json with the following:

  "extra": {
        "installer-paths": {
            "web/core": ["type:drupal-core"],
            "web/libraries/{$name}": ["type:drupal-library"],
            "web/modules/contrib/{$name}": ["type:drupal-module"],
            "web/profiles/contrib/{$name}": ["type:drupal-profile"],
            "web/themes/contrib/{$name}": ["type:drupal-theme"],
            "drush/contrib/{$name}": ["type:drupal-drush"]
        },
        "patches": {
            "asm89/stack-cors": {
                "Add check for same origin while identifying a CORS request" : "https://github.com/asm89/stack-cors/pull/11.patch"
            }
        }
    }
dawehner’s picture

Status: Active » Needs review
FileSize
3.33 KB

I'm wondering whether core should point to the fork containing the fix for now.

The alternative would be to actually override the classes. Sadly they are using private functions which makes things harder than it would have to be.

hampercm’s picture

Assigned: Unassigned » hampercm
Status: Needs review » Needs work

This finally got committed upstream! No more need for the work-around patch in #7.

Working on a standard composer.json patch...

hampercm’s picture

Assigned: hampercm » Unassigned

Ugh, just realized the release hasn't been cut yet. I pinged @asm86 on GitHub asking for that to be done.

navneet0693’s picture

Yeah! finally, it got merged. https://github.com/asm89/stack-cors/pull/11

hampercm’s picture

Status: Needs work » Needs review
FileSize
2.61 KB

Updated requirement for asm89/stack-cors in core/composer.json to to "~1.1"

dawehner’s picture

I am wondering whether we should have some form of regression / basic functional test coverage for that.

Wim Leers’s picture

#12 I wonder the same, but then again: shouldn't that be up to the upstream? We shouldn't have test coverage for every upstream bugfix, should we?

Difficult line to walk.

Wim Leers’s picture

Status: Needs review » Needs work
Issue tags: -Needs upstream bugfix +Needs tests

Although I guess in this case thanks to CorsIntegrationTest being added in #2850034: CORS allow-origin '*' not possible because of cached headers, it's now trivial to add this test coverage? So, let's do this.

dawehner’s picture

Well I agree there should be upstream unit testing ... but I think from the Drupal REST product point of view ensuring that enabling cors doesn't break the Drupal site makes sense, and as such having test coverage would be useful. Some simple form would be totally enough.

Wim Leers’s picture

You're absolutely right. Thanks for insisting on that!

hampercm’s picture

Assigned: Unassigned » hampercm
Issue summary: View changes

Working on a test for this...

hampercm’s picture

Adds a test to verify that POSTs with an "Origin" header the same as the site's base URL are permitted.

Interdiff for this patch is the same as the test-only patch

The last submitted patch, 18: upstream_cors_breaks-2853201-18-test-only.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 18: upstream_cors_breaks-2853201-18.patch, failed testing.

hampercm’s picture

hampercm’s picture

Status: Needs work » Needs review
hampercm’s picture

FileSize
954 bytes

Arg, forgot to attach interdiff :P

The last submitted patch, 21: upstream_cors_breaks-2853201-21-test-only.patch, failed testing.

dawehner’s picture

Status: Needs review » Needs work
+++ b/core/tests/Drupal/FunctionalTests/HttpKernel/CorsIntegrationTest.php
@@ -16,10 +16,26 @@
+   * @var \GuzzleHttp\ClientInterface
+   */
+  protected $httpClient;
+
+  /**
    * {@inheritdoc}
    */
   public static $modules = ['system', 'test_page_test', 'page_cache'];
 
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    // Set up a HTTP client that accepts relative URLs.
+    $this->httpClient = $this->container->get('http_client_factory')
+      ->fromOptions(['base_uri' => $this->baseUrl]);
+  }

@@ -72,6 +88,18 @@ public function testCrossSiteRequest() {
+    $response = $this->httpClient->request('POST', '/test-page', [
+      'headers' => [
+        'Origin' => $origin,
+      ]

You should get the client from mink directly: $client = $this->getSession()->getDriver()->getClient()->getClient();, see \Drupal\Tests\rest\Functional\ResourceTestBase::request

hampercm’s picture

hampercm’s picture

The last submitted patch, 26: upstream_cors_breaks-2853201-26-test-only.patch, failed testing.

The last submitted patch, 26: upstream_cors_breaks-2853201-26.patch, failed testing.

The last submitted patch, 27: upstream_cors_breaks-2853201-27-test-only.patch, failed testing.

dawehner’s picture

Status: Needs review » Reviewed & tested by the community
FileSize
3.88 KB
895 bytes

Here is a small improvement for the test, but all in all, this looks great.

alexpott’s picture

Version: 8.3.0-beta1 » 8.4.x-dev
Status: Reviewed & tested by the community » Fixed
Issue tags: +8.4.0 release notes

Committed 453d552 and pushed to 8.4.x. Thanks!

Unfortunately we can only bump minors in our own minor release so that makes this allegedly not patch safe. However there is something interesting here and worth thinking about.

+++ b/core/composer.json
@@ -32,7 +32,7 @@
-        "asm89/stack-cors": "~1.0"
+        "asm89/stack-cors": "~1.1"

By our current definitions 1.1 is compatible with ~1.0 - so what we're really changing here is the minimum version. So the question is can we change our minimum version in a patch release. Looking at https://github.com/asm89/stack-cors/compare/1.0.0...1.1.0 it drops PHP 5.3 and 5.4 support but non of the other changes would not be considered for a Drupal patch release.

I guess a mitigation is that work around is pretty simple.

Tagging with 8.4.0 release notes as I think library upgrades should be mentioned.

  • alexpott committed 453d552 on 8.4.x
    Issue #2853201 by hampercm, dawehner: [upstream] CORS breaks form...
dawehner’s picture

We could ask the author to release a 1.0.1 which has the fix applied.

alexpott’s picture

@dawehner yep that'd allow use to fix this in a patch release imo. Do you think it is worth it given the mitigation?

dawehner’s picture

@dawehner yep that'd allow use to fix this in a patch release imo. Do you think it is worth it given the mitigation?

Practically it makes it impossible to use CORS for real sites, I would argue.

It is never not worth to try: https://github.com/asm89/stack-cors/issues/44

hampercm’s picture

It seems to me that bug fixes should preempt the restriction against minor-version library updates. If something is broken in a way that is impacting usability significantly, it should be fixed.

I certainly see the point of this restriction, but in this case it feels like the people are serving the process, instead of the process serving the people.

Status: Fixed » Closed (fixed)

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

dawehner’s picture

Version: 8.4.x-dev » 8.3.x-dev
Status: Closed (fixed) » Needs review

Both contentacms and reserverouir ran into this specific problem. It makes it impossible for them to use cors. I think the theoretical possibility of a BC problem, vs. people running into a broken site is a bit ridiculous.

xjm’s picture

Version: 8.3.x-dev » 8.4.x-dev
Status: Needs review » Fixed

Today was the final normal bugfix release for 8.3.x. Aside from criticals and security fixes, no more commits will be made to 8.3.x, so marking this issue fixed again. Thanks!

Status: Fixed » Closed (fixed)

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

Wim Leers’s picture

Issue tags: +API-First Initiative