laravel-shopify
laravel-shopify copied to clipboard
App must set security headers to protect against clickjacking
Hello @osiset, My App was rejected by Shopify due to the bellow reason. please help.
App must set security headers to protect against clickjacking. Your app does not request installation on the shop immediately after clicking "add app". Apps must ask a shop for access when being installed on a shop for the first time, as well as when they are being reinstalled after having been removed. During install or reinstall we expected OAuth to be initiated at https://cambridgetestshop.myshopify.com/admin/oauth/request_grant but was redirected to https://cambridgetestshop.myshopify.com/admin/apps/0dc7ae777517adc333ebd1aeb6f5a556/authenticate/token?shop=cambridgetestshop.myshopify.com&target=%2F. Learn more about authentication in our developer documentation
I installed the package on fresh laravel and tried to install it on Shopify, but the URL changes. Please see below screenshot
Laravel version - 8.75.0 Package version - 17.1.0 php version - 8.0.11
Same issue. We got rejected because of the same reason. Any workaround to solve this?
Try phpclassic/php-shopify which is easier and better. You can have more control over the installation process.
Hi.
First off, let me tell you something. I use a translation app because I'm not very good at English. sorry.
The following header must be set for security purposes.
https://github.com/osiset/laravel-shopify/wiki/Installation#appbridge
You will also need to set the following header to allow iframes.
https://shopify.dev/apps/store/security/iframe-protection
The solution to the Content-Security-Policy header was discussed in this issue.
Migrate app from Shopify App Bridge 1.x to Shopify App Bridge 2.0
However, I used a different method to create the process and resubmit the application.
Hi, my app also got rejected because of the same reason. Please help.
I don't know this is a permanent solution or library will provide a better one but we have added a middleware for now to handle this problem and our app pass the initial automatic verification step after resubmission.
class FrameHeadersMiddleware
{
public function handle($request, Closure $next)
{
$response = $next($request);
$user = \Auth::user();
if($user){
$response->header('Content-Security-Policy', "frame-ancestors https://{$user->name} https://admin.shopify.com");
}
return $response;
}
}
[sarmadmakhdoom] is your app working with this solution ?
Thanks @sarmadmakhdoom this really worked
[sarmadmakhdoom] is your app working with this solution ?
Yeah it’s working fine after this. Do you see any issues after adding this middleware?
Hi @sarmadmakhdoom. Are you using this middleware globally or as a group middleware. I used this in group middleware, still my app got rejected for the same reason. Though I can see that the content-security-policy has been set in response headers for all the routes. What can be possibly wrong? Please help.
Hi @sarmadmakhdoom. Are you using this middleware globally or as a group middleware. I used this in group middleware, still my app got rejected for the same reason. Though I can see that the content-security-policy has been set in response headers for all the routes. What can be possibly wrong? Please help.
I am using it as global middleware, try that please
Hi @sarmadmakhdoom below is my middleware
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class FrameHeadersMiddleware
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
*/
public function handle(Request $request, Closure $next)
{
$response = $next($request);
$user = \Auth::user();
if($user){
$response->header('Content-Security-Policy', "frame-ancestors https://{$user->name} https://admin.shopify.com");
}
return $response;
}
}
Add below is my kernel.php, you can see that I have registered it as global middleware
<?php
namespace App\Http;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
class Kernel extends HttpKernel
{
/**
* The application's global HTTP middleware stack.
*
* These middleware are run during every request to your application.
*
* @var array<int, class-string|string>
*/
protected $middleware = [
// \App\Http\Middleware\TrustHosts::class,
\App\Http\Middleware\TrustProxies::class,
\Fruitcake\Cors\HandleCors::class,
\App\Http\Middleware\PreventRequestsDuringMaintenance::class,
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
\App\Http\Middleware\TrimStrings::class,
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
\App\Http\Middleware\FrameHeadersMiddleware::class,
];
I can see by inspecting the page that content-security-policy is being set on all the http requests. Still my app is getting auto rejected. I am lost here and have no idea about what to do.
Please help.
Hi @pangiras
Do you have X-Frame-Options set in your header?
Hi @inaryu
No I haven't set X-Frame_Options, and my application runs in App Bridge.
Hi @pangiras
Currently the laravel-shopify library has a bug when installing the app. https://github.com/osiset/laravel-shopify/issues/1071 This may be the reason why your application is rejected.
Hi @pangiras
Currently the laravel-shopify library has a bug when installing the app. #1071 This may be the reason why your application is rejected.
Hi @inaryu
I can confirm that this is not the problem, as I have implemented the solution mentioned by you in #1071 and that has fixed the bug.
@pangiras If so, there may be another cause. I also submitted my app after making this fix, and so far it has not been rejected. Do you have any special app settings?
Hi @inaryu and @sarmadmakhdoom. Thank you for the support provided, I really appreciate your effort and time.
I was able to get past the auto rejection using two fixes.
- I added the middleware as mentioned by @sarmadmakhdoom.
- I added the following header to this directory in my case /vendor/osiset/laravel-shopify/src/resources/views/layouts/default.blade.php , as I am extending default.blade.php in all the views.
<?php
$domain=$shopDomain ?? Auth::user()->name ;
header("Content-Security-Policy: frame-ancestors https://".$domain." "."https://admin.shopify.com");
?>
Hi @pangiras Glad we could work it out! By the way, I submitted an application with this response.
- Create middleware.
- Add middleware to the web middleware group
- Processing in middleware (app/Http/Middleware/AddContentSecurityPolicyHeader.php)
<?php
namespace App\Http\Middleware;
use App\Models\User;
use Closure;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Osiset\ShopifyApp\Objects\Values\ShopDomain;
class AddContentSecurityPolicyHeader
{
protected const ADMIN_SHOPIFY_URL = 'https://admin.shopify.com';
protected const HEADER_FORMAT = 'frame-ancestors %s %s';
public function handle(Request $request, Closure $next)
{
/** @var Response|RedirectResponse $response */
$response = $next($request);
// If it's not a redirect response and it's not Ajax
if ($response instanceof Response && !$request->ajax()) {
// If the request contains shop data, get the shop data
if ($request->has('shop')) {
$shopDomain = ShopDomain::fromNative($request->get('shop'));
} elseif ($request->user() instanceof User) {
// Get from user data
$shopDomain = $request->user()->getDomain();
} else {
// If you still can't get it, get it from the request data.
$shopDomain = ShopDomain::fromRequest($request);
}
if ($shopDomain instanceof ShopDomain) {
$response->header('Content-Security-Policy', sprintf(self::HEADER_FORMAT, 'https://' . $shopDomain->toNative(), self::ADMIN_SHOPIFY_URL));
}
}
return $response;
}
}
- Add middleware to the web middleware group in app/Http/Kernel.php
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
// \Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
// add middleware
\App\Http\Middleware\AddContentSecurityPolicyHeader::class,
],
'api' => [
'throttle:api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
];
I think adding it to the web middleware group will handle most of the requests. This also means that you probably don't need to add it to your layout file.
Hi @inaryu ,
I also first started out by creating the group middleware and did add into web middleware group and I could see that all the requests made had content-security-policy set, still the app got auto rejected. And then I changed it to global middleware to eliminate any doubts, still the app got auto rejected. And then I tried the fix that solved my problem. Though it isn't the best practice as I can see that in some requests the csp is repeated, but that won't do any harm I suppose. I like the way you have implemented the middleware, may it also help others facing the same problem.
Hi @inaryu and @sarmadmakhdoom. Thank you for the support provided, I really appreciate your effort and time.
I was able to get past the auto rejection using two fixes.
- I added the middleware as mentioned by @sarmadmakhdoom.
- I added the following header to this directory in my case /vendor/osiset/laravel-shopify/src/resources/views/layouts/default.blade.php , as I am extending default.blade.php in all the views.
<?php $domain=$shopDomain ?? Auth::user()->name ; header("Content-Security-Policy: frame-ancestors https://".$domain." "."https://admin.shopify.com"); ?>
You’re welcome. I’m glad I was able to help
@inaryu @sarmadmakhdoom - Feel free to submit a PR to help fix this issue for the wider package.
I followed the as mentioned by @sarmadmakhdoom. But still app gets rejected.
Anyone know what is the issue ?
Rejection can be based on a number of things - Shopify will tell you why in their response to you. So you would need to look at that and see what you need to do.
The everytime getting same feedback-
App must set security headers to protect against clickjacking. Your app must set the proper frame-ancestors content security policy directive to avoid clickjacking attacks. The 'content-security-policy' header should set frame-ancestors https://[shop].myshopify.com/ https://admin.shopify.com/, where [shop] is the shop domain the app is embedded on.
I can see by inspecting the page that content-security-policy is being set on all the http requests
@ingalesachin7 did you find any solution? I am getting the same response in every rejection.
https://github.com/osiset/laravel-shopify/issues/1070#issuecomment-1039959161
I used this solution mentioned by @inaryu And
<?php
$domain=$shopDomain ?? Auth::user()->name ;
header("Content-Security-Policy: frame-ancestors https://".$domain." "."https://admin.shopify.com");
?>
added this on layout page.
Solution is accpeted.
Released in v17.2.0