Clickjacking headers for shopify app

TheYellowDellow
Shopify Partner
1 0 2

Hi there,

Recently I attempted to submit an alternative payment gateway to the shopify store and was almost immediately rejected with the following:

 

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.

And I'm wondering - do these headers have to be present on the installation of the app?

Or are we expected to set them upon redirect to the app for the alternative payment gateway flow. Just wondering where in the order of operations these need to exist.

Replies 32 (32)

Balouchi
Excursionist
44 0 8

Hi did you manage to resolve this issue ?

Anglia
Tourist
5 0 2

Hi  

Is it OK to display this in the response header?

 

content-security-policy frame-ancestors https://*.myshopify.com https://admin.shopify.com

Balouchi
Excursionist
44 0 8

how can i resolve this issue because that my app was rejected constantly PLz Guaid 

  1. The 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.
  2. where I have put this code in an over the app
Anglia
Tourist
5 0 2

I don't quite understand what you mean. Can you explain it more clearly? Thanks.

Balouchi
Excursionist
44 0 8

Thanks for Replay 
Actually I have made a public app on Shopify partner, when I send that app for review, then this error is there. So do you have any idea about this error?

Balouchi
Excursionist
44 0 8

I am creating a public app and when it was sent for review this is the error which we are facing right now. Can you please help me in resolving it thanks.

The Error:

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.

And I'm wondering - do these headers have to be present on the installation of the app?

Or are we expected to set them upon redirect to the app for the alternative payment gateway flow. Just wondering where in the order of operations these need to exist.

juanmaptag
Tourist
10 1 4

Hi

 

I had the same issue and what solved that error for me was adding the headers in the next.config file, I did it exatcly using the NextJS documentation here

 

Where the key is "Content-Security-Policy" and the value is "frame-ancestors https://[shop].myshopify.com https://admin.shopify.com/;"

Balouchi
Excursionist
44 0 8

I have tried the link you shared Luckily I am using NextJs but still My account got rejected after review with same issue. What should I do now ?

DenisSabolotni
Shopify Partner
12 1 20

I have a question. How did you pass the value for [shop] to the headers function in next.config.js,
because it's should be something like this: "frame-ancestors https://example-shop.myshopify.com https://admin.shopify.com/;

Balouchi
Excursionist
44 0 8

I have used the value as mentioned in the link.

Can you share an example of how you manage to create it ?

Thanks in advance

DenisSabolotni
Shopify Partner
12 1 20

I don't know if this will help you, but this is how I tried to solve the problem (I'm still waiting for the review of my app):

 

I assume you also use React.js if you have the next.config.js file in your project.

 

In server.js I have defined a new function:

 

  // Defense against clickjacking: https://cheatsheetseries.owasp.org/cheatsheets/Clickjacking_Defense_Cheat_Sheet.html
  // https://shopify.dev/apps/store/security/iframe-protection
  // Only allow Shop host url to display site in iFrame
  const setClickJackingHeadersMiddleware = async (ctx, next) => {
    const shop = ctx.query.shop;
    try {
      if (shop) {
        ctx.set({
          "Content-Security-Policy": `frame-ancestors https://${shop} https://admin.shopify.com;`,
        });
        console.log(
          "Set Content-Security-Policy to: ",
          `frame-ancestors https://${shop} https://admin.shopify.com;`
        );
      } else {
      }
    } catch (err) {
      console.log("Doesnt matter");
    } finally {
      return next();
    }
  };

 

And this function is called by the router before any other router is defined:

 

router.use(setClickJackingHeadersMiddleware);

 

I will keep you updated if it has worked.
I could also create a gist on Github so you can see my complete server.js file if you want to.

Balouchi
Excursionist
44 0 8

Sure thanks will let u know

Balouchi
Excursionist
44 0 8

You are also trying for public app right ? or embedded ?

Balouchi
Excursionist
44 0 8

same error again

 Can You Please Help me in resolving this issue ? I am stuck here. Help is appreciated.

Thanks

ISSUE

Requirements that must be met before initial screening
  1. 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.
Balouchi
Excursionist
44 0 8

I have passed the information exactly the same way. Still same issue What should i do now please help

LeoNeo
Tourist
7 0 1

we are same boat, I wonder this forum has no official support reply in a week.....

Balouchi
Excursionist
44 0 8

But your issue was resolved right ?

Hey can you do me a favor like if you can share your coding with me regarding this issue only.

This would mean alot.

Thanks

LeoNeo
Tourist
7 0 1

still reject from Shopify, but the email don't specific which page with url encounter the problem, it quite bad dev experience.

I 100% sure I add the following line on every page and using F12 dev tool to check the header is exist.

 

to add the header is simple add

header("Content-Security-Policy: frame-ancestors https://".$_GET['shop']." https://admin.shopify.com;");
and resulting
Content-Security-Policy: frame-ancestors https://abcshop.myshopify.com https://admin.shopify.com;
 
it really bad
 
Balouchi
Excursionist
44 0 8

OK Thanks

I will update you after checking

rohit_martires
Shopify Partner
49 3 7

nice, ill try this out

1080
Shopify Partner
282 8 39

@DenisSabolotni  can you please send the screenshot of network tab request with  header 

LeoNeo
Tourist
7 0 1

I also have these issue and I ensure I have add the header via PHP on EVERY page

header("Content-Security-Policy: frame-ancestors https://".$_GET['shop']." https://admin.shopify.com;");
 
I thought there is some bug of Shopify automate test system

 

juanmaptag
Tourist
10 1 4

Hi guys!

 

I have found a solution that passed the review!

 

I coudn't find a way to insert a variable in the next.config file, if that variable wasnt defined in the dotenv file.

 

Now I'll share a screen shot of how did I do it. (I am using a project generated with the Shopify CLI)

 

In server.js

juanmaptag_0-1641224877687.png

 

Hopefully this help you out guys!

Balouchi
Excursionist
44 0 8

Ok will get to you on this one.

you were using Next.js right ?

DenisSabolotni
Shopify Partner
12 1 20

@juanmaptag so I tried your solution, but this will resolve in another error when trying to install the app in an test shop:

 

Unhandled Runtime Error

AppBridgeError: APP::ERROR::INVALID_CONFIG: host must be provided

 

and the Shopify review is getting declined with the following reason: 

Required changes
  1. We were redirected to an error page when attempting to install your app. Please test your app's installation on a development store using OAuth before resubmitting. Review this example of what the required installation flow looks like and see this screencast for context on your app. 

DenisSabolotni
Shopify Partner
12 1 20

I have finally found a working solution (all code changes are made in server.js):

 

I'm using the npm package koa-helmet (v6.1.0) (not necessary, can also be done without)

 

 

import helmet from "koa-helmet";

 

 

 

and almost right after the app.prepare statement, I have defined a new middleware function called setContentSecurityHeader. The problem with my other solution before was, that it has not always initialized the Clickjacking header correctly, especially before authentication, the header has not been set.

 

 

app.prepare().then(async () => {
  const server = new Koa();
  const router = new Router();

  const setContentSecurityHeader = (ctx, next) => {
    // Cookie is set after auth
    if (ctx.cookies.get("shopOrigin")) {
      return helmet.contentSecurityPolicy({
        directives: {
          defaultSrc: helmet.contentSecurityPolicy.dangerouslyDisableDefaultSrc,
          frameAncestors: [
            `https://${ctx.cookies.get("shopOrigin")}`,
            "https://admin.shopify.com",
          ],
        },
      })(ctx, next);
    } else {
      // Before auth => no cookie set...
      return helmet.contentSecurityPolicy({
        directives: {
          defaultSrc: helmet.contentSecurityPolicy.dangerouslyDisableDefaultSrc,
          frameAncestors: [
            `https://${ctx.query.shop}`,
            "https://admin.shopify.com",
          ],
        },
      })(ctx, next);
    }
  };

  server.use(setContentSecurityHeader);

 

 

 

And as a final step, I initialize a cookie right after authentication. This cookie ensures, that the header is set correctly for each following request after authentication.

 

 

  server.use(
    createShopifyAuth({
      async afterAuth(ctx) {
        const { shop, accessToken, scope } = ctx.state.shopify;
        // set shopOrigin cookie, so it can be used for click jacking header
        ctx.cookies.set("shopOrigin", shop, {
          httpOnly: false,
          secure: true,
          sameSite: "none",
        });
        const host = ctx.query.host;
        ACTIVE_SHOPIFY_SHOPS[shop] = scope;

 

 

 

Hope this helps.

soren121
Visitor
1 0 0

@DenisSabolotni  Does this solution really pass app review? Aren't third-party cookies forbidden in embedded apps as of this year?

rohit_martires
Shopify Partner
49 3 7

yea i tried this and it does work, did pass through the review

itz_umang
Shopify Partner
4 0 1

Hi, To solve the clickjacking headers issue in next/react use below code in getInitialProps in _app.js. Thanks.


ctx.res.setHeader('Content-Security-Policy',`frame-ancestors https://${ctx.query.shop} https://admin.shopify.com`);

 

MyApp.getInitialProps = async ({ ctx }) => {
  ctx.res.setHeader('Content-Security-Policy',`frame-ancestors https://${ctx.query.shop} https://admin.shopify.com`);
  return {
    shop: ctx.query.shop,
    host: ctx.query.host,
  };
};

ameernormie
Shopify Partner
4 0 1

There are two solutions for this if you're working with next app with node.js. Both solutions worked for me.

First Solution:

In your server file add the following code:

appRouter.get("(.*)", async (ctx) => {
  const shop = ctx.query.shop as string;

  if (shop) ctx.set("Content-Security-Policy", `frame-ancestors https://${shop} https://admin.shopify.com`);
    await handleRequest(ctx);
});

 

Second Solution:

Add the following code to your home page in next.js. It will be an index.tsx, index.jsx file in your pages directory

export async function getServerSideProps(context: any) {
  if (context?.query?.shop)
    context.res.setHeader(
      "Content-Security-Policy",
      `frame-ancestors https://${context.query.shop} https://admin.shopify.com`,
    );
  return {
    props: {},
  };
}

DenisSabolotni
Shopify Partner
12 1 20

Hello there,

I just wanted to update you all, that this is not a problem anymore if you generate your project with the most recent Shopify-CLI.
The code is already there and it looks like this:

 

 

app.use((req, res, next) => {
    const shop = req.query.shop;
    if (Shopify.Context.IS_EMBEDDED_APP && shop) {
      res.setHeader(
        "Content-Security-Policy",
        `frame-ancestors https://${shop} https://admin.shopify.com;`
      );
    } else {
      res.setHeader("Content-Security-Policy", `frame-ancestors 'none';`);
    }
    next();
  });

 

 

 

 

The code looks very different in comparison to the older versions, so if you want to look into that in detail, just get the newest Shopify CLI version, generate a new project and copy the code parts you need.

 

Happy that Shopify reacted to feedback and now we don't have to deal with that problem anymore.

DaviAreias
Shopify Partner
37 0 6

I tried to submit my second app today, and suddently started getting this error again.

 

I'm now afraid to submit the app again, because shopify bans your account if your app gets rejected too many times. I'm using a solution similar to your last post on all routes and still get this error:

 

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.