This repository has been archived by the owner on Nov 30, 2022. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 376
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Setting up Iframe protection (#1178)
- Loading branch information
Showing
3 changed files
with
136 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
<?php | ||
|
||
namespace Osiset\ShopifyApp\Http\Middleware; | ||
|
||
use Closure; | ||
use Illuminate\Http\Request; | ||
use Illuminate\Support\Facades\Cache; | ||
use Osiset\ShopifyApp\Contracts\Queries\Shop as IShopQuery; | ||
use Osiset\ShopifyApp\Objects\Values\ShopDomain; | ||
|
||
/** | ||
* Responsibility for protection against clickjaking | ||
*/ | ||
class IframeProtection | ||
{ | ||
/** | ||
* The shop querier. | ||
* | ||
* @var IShopQuery | ||
*/ | ||
protected $shopQuery; | ||
|
||
/** | ||
* Constructor. | ||
* | ||
* @param IShopQuery $shopQuery The shop querier. | ||
* | ||
* @return void | ||
*/ | ||
public function __construct( | ||
IShopQuery $shopQuery | ||
) { | ||
$this->shopQuery = $shopQuery; | ||
} | ||
|
||
/** | ||
* Set frame-ancestors header | ||
* | ||
* @param Request $request The request object. | ||
* @param \Closure $next The next action. | ||
* | ||
* @return mixed | ||
*/ | ||
public function handle(Request $request, Closure $next) | ||
{ | ||
$response = $next($request); | ||
|
||
$shop = Cache::remember( | ||
'frame-ancestors_'.$request->get('shop'), | ||
now()->addMinutes(20), | ||
function () use ($request) { | ||
return $this->shopQuery->getByDomain(ShopDomain::fromRequest($request)); | ||
} | ||
); | ||
|
||
$domain = $shop | ||
? $shop->name | ||
: '*.myshopify.com'; | ||
|
||
$response->headers->set( | ||
'Content-Security-Policy', | ||
"frame-ancestors https://$domain https://admin.shopify.com" | ||
); | ||
|
||
return $response; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
<?php | ||
|
||
namespace Osiset\ShopifyApp\Test\Http\Middleware; | ||
|
||
use Illuminate\Auth\AuthManager; | ||
use Illuminate\Http\Request; | ||
use Illuminate\Http\Response; | ||
use Osiset\ShopifyApp\Http\Middleware\IframeProtection; | ||
use Osiset\ShopifyApp\Storage\Queries\Shop as ShopQuery; | ||
use Osiset\ShopifyApp\Test\TestCase; | ||
|
||
class IframeProtectionTest extends TestCase | ||
{ | ||
/** | ||
* @var AuthManager | ||
*/ | ||
protected $auth; | ||
|
||
public function setUp(): void | ||
{ | ||
parent::setUp(); | ||
|
||
$this->auth = $this->app->make(AuthManager::class); | ||
} | ||
|
||
public function testIframeProtectionWithAuthorizedShop(): void | ||
{ | ||
$shop = factory($this->model)->create(); | ||
$this->auth->login($shop); | ||
|
||
$domain = auth()->user()->name; | ||
$expectedHeader = "frame-ancestors https://$domain https://admin.shopify.com"; | ||
|
||
$request = new Request(); | ||
$shopQueryStub = $this->createStub(ShopQuery::class); | ||
$shopQueryStub->method('getByDomain')->willReturn($shop); | ||
$next = function () { | ||
return new Response('Test Response'); | ||
}; | ||
|
||
$middleware = new IframeProtection($shopQueryStub); | ||
$response = $middleware->handle($request, $next); | ||
$currentHeader = $response->headers->get('content-security-policy'); | ||
|
||
$this->assertNotEmpty($currentHeader); | ||
$this->assertEquals($expectedHeader, $currentHeader); | ||
} | ||
|
||
public function testIframeProtectionWithUnauthorizedShop(): void | ||
{ | ||
$expectedHeader = 'frame-ancestors https://*.myshopify.com https://admin.shopify.com'; | ||
|
||
$request = new Request(); | ||
$shopQuery = new ShopQuery(); | ||
$next = function () { | ||
return new Response('Test Response'); | ||
}; | ||
|
||
$middleware = new IframeProtection($shopQuery); | ||
$response = $middleware->handle($request, $next); | ||
$currentHeader = $response->headers->get('content-security-policy'); | ||
|
||
$this->assertNotEmpty($currentHeader); | ||
$this->assertEquals($expectedHeader, $currentHeader); | ||
} | ||
} |