【问题标题】:How to solve HMAC Validation Failed in Shopify App?如何解决 Shopify 应用中的 HMAC 验证失败?
【发布时间】:2019-10-21 03:31:03
【问题描述】:

我完全按照 shopify 文档 (Shopify app with Node and Express) 中的代码将应用程序与 express 集成,但似乎我仍然遇到 HMAC 验证失败。

const map = Object.assign({}, req.query);
delete map['signature'];
delete map['hmac'];
const message = querystring.stringify(map);
const providedHmac = Buffer.from(hmac, 'utf-8');
const generatedHash = Buffer.from(
  crypto
    .createHmac('sha256',this.configService.get('SHOPIFY_API_SECRET'))
    .update(message)
    .digest('hex'),
  'utf-8'
);
let hashEquals = false;
// timingSafeEqual will prevent any timing attacks. Arguments must be buffers
try {
  hashEquals = crypto.timingSafeEqual(generatedHash, providedHmac)
  // timingSafeEqual will return an error if the input buffers are not the same length.
} catch (e) {
  hashEquals = false;
};

if (!hashEquals) {
  return res.status(400).send('HMAC validation failed');
}

我希望上面的代码能够正常工作并且不会返回错误。

【问题讨论】:

    标签: node.js express shopify hmac shopify-app


    【解决方案1】:

    我遇到了同样的问题,如果我们在重定向验证成功时将此留空,我认为这可能与如何创建用于 state 参数的 nonce 有关。从一开始我就创建了一个这样的随机数:

    const nonce: string = crypto.randomBytes(16).toString('base64');
    

    然后如果我在 state 参数中使用这个随机数,回调中的 hmac 验证总是失败,当我将其更改为:

    const nonce: string = crypto.randomBytes(16).toString('hex');
    

    一切都按预期进行:

    @Get('auth/callback')
    callback(@Request() req) {
      // req.state should be your previously created nonce
      const { hmac, ...params } = req;
      if (this.validateHmac(hmac, params)) {
        // Validation success
        return 'OK';
      }
    }
    
    private validateHmac(hmac: string, data: string): boolean {
      const hash: string = Buffer.from(
        crypto.createHmac('SHA256', this.appSecret)
          .update(Buffer.from(data, 'utf8'))
          .digest('hex'),
        'utf-8'
      );
      return crypto.timingSafeEqual(hash, Buffer.from(hmac, 'utf-8'));
    }
    

    【讨论】:

      【解决方案2】:

      这应该适用于您的 Express 应用程序。

        // Packages should be downloaded - npm i query-string crypto -S
        import queryString from "query-string";
        import crypto from "crypto";
        
        ....
      
        const queryObj = Object.assign({}, req.query);
        const { signature: _ signature, hmac, ...map } = queryObj;
      
        const orderedMap = Object.keys(map)
          .sort((value1, value2) => value1.localeCompare(value2))
          .reduce((accum: any, key: string) => {
            accum[key] = map[key];
            return accum;
          }, {});
      
        const message = queryString.stringify(orderedMap);
        const generatedHash = crypto.createHmac("sha256", SHOPIFY_CLIENT_SECRET).update(message).digest("hex");
      
        // Safe Compare
        const aLen = Buffer.byteLength(generatedHash);
        const bLen = Buffer.byteLength(hmac);
      
        if (aLen !== bLen) {
          return res.status(400).send('HMAC validation failed');
        }
      
        // Turn strings into buffers with equal length
        // to avoid leaking the length
        const buffA = Buffer.alloc(aLen, 0, "utf8");
        buffA.write(stringA);
        const buffB = Buffer.alloc(bLen, 0, "utf8");
        buffB.write(stringB);
      
        const valid = crypto.timingSafeEqual(buffA, buffB);
        if(!valid) {
            return res.status(400).send('HMAC validation failed');
        }
      
        ....
      
      

      基于koa repository

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-09-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多