SHOPIFY APP , Clickjacking, App must set security headers

Balouchi
Excursionist
44 0 8

Hi there,

Requirement that must be met before initial screening.

 

App must set security headers to protect against click jacking.
Your app must set the proper frame-ancestors content security policy directive to avoid click jacking 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.

 

Can anyone please help in resolving this issue. I am not able to resolve it please provide coding or solution to this .

Replies 21 (21)

fifi15
Tourist
6 0 2

i have same problem 😞 , any solution ?

Balouchi
Excursionist
44 0 8
Not yet still waiting. If you come up with any do let us know as well.

aravindsundar
Shopify Partner
9 0 0

Having the same issue. Couldn't able to find any solution

Balouchi
Excursionist
44 0 8

If you come up with any solution please let me know thanks

aravindsundar
Shopify Partner
9 0 0

Sure !!

Balouchi
Excursionist
44 0 8

Thanks Likewise 🙂

ShopSappers
Excursionist
10 0 4

Hi there,

Like they mentioned in the initial screening you need to add CSP headers from your server side.

https://shopify.dev/apps/store/security/iframe-protection

The code is depends on the programming language you use for your app.

You need to set a header with the current requesting store domain.

aravindsundar
Shopify Partner
9 0 0

I am using PHP as my backend. Can you pls check is this format ?

header('X-Frame-Options:self');

<Content-Security-Policy: frame-ancestors 'self'>

or 

<Content-Security-Policy: frame-ancestors 'https://[shop].myshopify.com'>

Should I use this header for all the pages in my app or for particular index page which connects to the iframe?

miguelcabgil
Shopify Partner
12 0 7

Hey there. I'm experiencing the same problem. What worked for you?

DenisSabolotni
Shopify Partner
12 1 20

If you are using React with Koa this is the solution (all 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

 

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.

Other resources:
https://shopify.dev/apps/store/security/iframe-protection

miguelcabgil
Shopify Partner
12 0 7

Thank you for your response, Denis. As i see, we must set this header in each endpoint concerned to Shopify. Am i right?

DenisSabolotni
Shopify Partner
12 1 20

Exactly.
My problem was that the header was not set for each and every request...

miguelcabgil
Shopify Partner
12 0 7

Sure! And I still have the problem. I think the preflight check (OPTIONS verb) is my headache. Let's keep this post up to date to help other devs to save time

miguelcabgil
Shopify Partner
12 0 7

I finally made it. Using express i created a middleware to write Content-Security-Policy header as they required on absolutely ALL endpoints, with the next code:

export const CspMiddleware = async (req: Request, res: Response, next: NextFunction) => {
const merchant = req.query?.shop.toString() as string;
res.setHeader('Content-Security-Policy', `frame-ancestors ${merchant} https://admin.shopify.com;`);
next();
};

And i had to create an options route to cover the preflight-check requests made by shopify, with the next code:

api.options('*', CspHeaderMiddleware, your-controller-here);

 Remember: you have to place this endpoint on top of all of the other endpoints in file. Cheers

marti291
Shopify Partner
2 0 1

Dennis, thanks for posting, super helpful! 

DaviAreias
Shopify Partner
39 0 6

This seems to work together with @miguelcabgil solution.

 

The problem that I'm facing now is that if I restart the server, and try to use the app again, it goes in a redirect auth loop until I clear cookies. I imagine that if I ever have to restart the app after it's deployed, customers will probably get stuck in the loop. I don't understand how are the cookies causing a loop.

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 and look into it yourself.

 

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

sonchu
Shopify Partner
7 0 5

I used that latest Shopify CLI with the above code but still receive clickjacking rejection. 

sonchu_0-1660653502868.jpeg

And @miguelcabgil suggested installing the header on ALL endpoints, I guess it includes API endpoints too. 

Hesham1
Shopify Partner
3 0 0

hi @sonchu , have you tried setting up the header on all endpoints and got approval?

sonchu
Shopify Partner
7 0 5

I did. 

Hesham1
Shopify Partner
3 0 0

thanks @sonchu appreciated your reply