Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Resolve issue #118 CORS issues in IE and Edge #119

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1634,6 +1634,21 @@ Once you have enabled CORS you can then control four new headers in the HTTP Res
Max-Age: 600
```

5. **CORS exception for Internet Explorer and Edge**

IE 11 and Edge fix. When CORS is enabled but we are on the same domain,
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs an update as this is a copy-paste from the inline comment

IE refuses to send the Origin header, causing a lot of pain.

The next best thing, is to validate it's IE or Edge and check the referrer.
This is not the safest solution, but it's a workaround.

Note, when it's an actual CORS request, IE does add the header,
so we only use this when it is not an actual CORS request.

[Bug on Microsoft.com](https://connect.microsoft.com/IE/feedback/details/781303/origin-header-is-not-added-to-cors-requests-to-same-domain-but-different-port)

Observed is that this also happens when the port is the same.

### Sample Custom CORS Config

```yaml
Expand Down
38 changes: 36 additions & 2 deletions src/Controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use SilverStripe\Control\HTTPResponse;
use SilverStripe\Core\Config\Config;
use SilverStripe\Control\Director;
use SilverStripe\Dev\Debug;
use SilverStripe\GraphQL\Auth\Handler;
use SilverStripe\Versioned\Versioned;
use Exception;
Expand Down Expand Up @@ -182,8 +183,41 @@ public function addCorsHeaders(HTTPRequest $request, HTTPResponse $response)
return $this->httpError(403, "Access Forbidden");
}
} else {
// No Origin header present in Request.
return $this->httpError(403, "Access Forbidden");
/**
* IE 11 and Edge fix. When CORS is enabled but we are on the same domain,
* IE refuses to send the Origin header, causing a lot of pain.
* The next best thing, is to validate it's IE or Edge and check the referrer.
* This is not the best way to go, but it's something
* Note, when it's an actual CORS request, IE does add the header, so we only end up here
* only when it is not an actual CORS request
* @link https://connect.microsoft.com/IE/feedback/details/781303/origin-header-is-not-added-to-cors-requests-to-same-domain-but-different-port
* This fault also happens when the port is the same!
*/
$isIE = strpos($request->getHeader('User-Agent'), 'Trident') !== false;
$isEdge = strpos($request->getHeader('User-Agent'), 'Edge') !== false;
// Only go in here if we have a referer and a Microsoft browser
if (
($isEdge || $isIE) &&
$request->getHeader('referer')
) {
$refererURL = parse_url($request->getHeader('referer'));
// We only take the host, as the http(s) part _may_ be omitted
if (
in_array('*', $allowedOrigins, true) ||
// Check if either with or without scheme is in the allowed origins
(in_array($refererURL['host'], $allowedOrigins, true) ||
// Strict checking on scheme, for security
in_array($refererURL['scheme'] . '://' . $refererURL['host'], $allowedOrigins, true))
) {
// It's IE/Edge, referer matches or it's "allow all", so set the origin
$response->addHeader('Access-Control-Allow-Origin', $refererURL['scheme'] . '://' . $refererURL['host']);
}
}
// The needs parsing to get the base URL
else {
// No Origin or IE plus valid Referer header present in Request.
return $this->httpError(403, "Access Forbidden");
}
}
} else {
// No allowed origins, ergo all origins forbidden.
Expand Down
95 changes: 95 additions & 0 deletions tests/ControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,101 @@ public function testAddCorsHeadersResponseCORSDisabled()
$this->assertEquals('405', $response->getStatusCode());
}

/**
* IE and Edge are a bit weird, so we check CORS headers differently on the same domain
* despite CORS being enabled. This checks if our IE exception plays nice
*/
public function testIECorsHeadersResponseCORSEnabledOnSameDomain()
{
Controller::config()->set('cors', [
'Enabled' => true,
'Allow-Origin' => 'localhost',
'Allow-Headers' => 'Authorization, Content-Type',
'Allow-Methods' => 'GET, POST, OPTIONS',
'Max-Age' => 86400
]);

$controller = new Controller();
$request = new HTTPRequest('OPTIONS', '');
$request->addHeader('referer', 'http://localhost/admin/tests');
$request->addHeader('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; Touch; rv:11.0) like Gecko');
$response = $controller->index($request);

$this->assertTrue($response instanceof HTTPResponse);
$this->assertEquals('200', $response->getStatusCode());
$this->assertNotEmpty($response->getHeader('Access-Control-Allow-Origin'));
}

/**
* IE and Edge are a bit weird, so we check CORS headers differently on the same domain
* despite CORS being enabled. This checks if our IE exception plays not nice without the user agent
* @expectedException \SilverStripe\Control\HTTPResponse_Exception
*/
public function testIECorsHeadersResponseCORSEnabledNoReferer()
{
Controller::config()->set('cors', [
'Enabled' => true,
'Allow-Origin' => 'localhost',
'Allow-Headers' => 'Authorization, Content-Type',
'Allow-Methods' => 'GET, POST, OPTIONS',
'Max-Age' => 86400
]);

$controller = new Controller();
$request = new HTTPRequest('OPTIONS', '');
$request->addHeader('referer', 'http://localhost/admin/tests');
$controller->index($request);
}

/**
* IE and Edge are a bit weird, so we check CORS headers differently on the same domain
* despite CORS being enabled. This checks if our IE exception plays nice
*/
public function testEdgeCorsHeadersResponseCORSEnabledOnSameDomain()
{
Controller::config()->set('cors', [
'Enabled' => true,
'Allow-Origin' => 'localhost',
'Allow-Headers' => 'Authorization, Content-Type',
'Allow-Methods' => 'GET, POST, OPTIONS',
'Max-Age' => 86400
]);

$controller = new Controller();
$request = new HTTPRequest('OPTIONS', '');
$request->addHeader('referer', 'http://localhost/admin/tests');
// Yes, that is a real Edge user agent string!
$request->addHeader('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36 Edge/15.15063');
$response = $controller->index($request);

$this->assertTrue($response instanceof HTTPResponse);
$this->assertEquals('200', $response->getStatusCode());
$this->assertNotEmpty($response->getHeader('Access-Control-Allow-Origin'));
}

/**
* IE and Edge are a bit weird, so we check CORS headers differently on the same domain
* despite CORS being enabled. This checks if our IE exception plays nice
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's the Edge exception, not IE

* @expectedException \SilverStripe\Control\HTTPResponse_Exception
*/
public function testEdgeCorsHeadersResponseCORSEnabledNoReferer()
{
Controller::config()->set('cors', [
'Enabled' => true,
'Allow-Origin' => 'localhost',
'Allow-Headers' => 'Authorization, Content-Type',
'Allow-Methods' => 'GET, POST, OPTIONS',
'Max-Age' => 86400
]);

$controller = new Controller();
$request = new HTTPRequest('OPTIONS', '');
// Yes, that is a real Edge user agent string!
$request->addHeader('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36 Edge/15.15063');
$controller->index($request);
}


protected function getType(Manager $manager)
{
return (new TypeCreatorFake($manager))->toType();
Expand Down