Shopify HMAC 验证在 Webhook 实施中失败

问题描述 投票:0回答:3

我正在开发 Shopify 应用程序。我正在尝试使用此函数使用 Shopify 的 HMAC 身份验证来验证应用程序上发生的 Shopify 交易。 Webhook 是通过主题 app_subscriptions/updateShopify Webhook API 创建的,shopifyApiSecret 是我们 Shopify 应用程序的客户端密钥。这是我们正在使用的代码片段,

async function validateHmac(req: Request) {
  let shopifyApiSecret = config.shopifyAppSecret;

  let hmac: any = req.headers["x-shopify-hmac-sha256"];
  const message = JSON.stringify(req.body);
  const generatedHash = crypto
    .createHmac("sha256", shopifyApiSecret)
    .update(message)
    .digest("base64");
  console.log({ message, generatedHash, hmac });

  const signatureOk = crypto.timingSafeEqual(
    Buffer.from(generatedHash),
    Buffer.from(hmac)
  );
  if (signatureOk) {
    return true;
  } else {
    return false;
  }
}

我们尝试比较这两种方法,即使用 === 以及使用timingSafeEqual,但该函数总是返回 false 并且在检查时 generatedHash 和 hmac 不相等。谁能告诉我这个实现是否有任何问题?预先感谢。

node.js webhooks hmac shopify-app
3个回答
1
投票

这是我们在.Net 中完成的实现。你可以从下面的代码中理解其中的逻辑:

string hmacHeader = Convert.ToString(Request.Headers["X-Shopify-Hmac-SHA256"]);
string requestJson = JsonSerializer.Serialize(request);

HMACSHA256 hmac = new HMACSHA256(Encoding.UTF8.GetBytes(APISecretKey));
string hash = Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(requestJson)));

isSuccess = string.Equals(hmacHeader, hash, StringComparison.OrdinalIgnoreCase);

0
投票

问题在于我们生成消息所使用的数据。 我们可以通过使用路由器中的express功能来解决这个问题

express.json({
  limit: '10mb',
  verify: (req, _res, buf) => {
    (req as any).rawBody = buf;
  },
});

并将 rawBody 传递给更新函数。

async function validateHmac(req: Request) {
  let shopifyApiSecret = config.shopifyAppSecret;

  let hmac: any = req.headers["x-shopify-hmac-sha256"];
  const message = req.rawBody;
  const generatedHash = crypto
    .createHmac("sha256", shopifyApiSecret)
    .update(message)
    .digest("base64");
  console.log({ message, generatedHash, hmac });

  const signatureOk = crypto.timingSafeEqual(
    Buffer.from(generatedHash),
    Buffer.from(hmac)
  );
  if (signatureOk) {
    return true;
  } else {
    return false;
  }
}

0
投票

这是 .NET 8 使用依赖注入获取共享密钥的解决方案。

创建一个

ShopifySettings
类:

public class ShopifySettings
{
    public string ApiSecretKey { get; set; }
}

然后将设置放入您的

appsettings.json

"ShopifySettings": {
    "ApiSecretKey": "<YOUR SECRET>",
}

在您的

Program.cs
文件中,在
var builder = WebApplication.CreateBuilder(args);
之后添加以下内容,在
var app = builder.Build();
之前添加以下内容:

builder.Services.Configure<ShopifySettings>(builder.Configuration.GetSection(nameof(ShopifySettings)));

以及

var app = builder.Build();
之后和
app.MapControllers();
之前:

app.Use(next => context =>
{
    context.Request.EnableBuffering();
    return next(context);
});

然后创建

VerifyShopifyAttribute
类:

public class VerifyShopifyAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext actionContext)
    {
        if (!(ValidateHash(actionContext)))
        {
            throw new BadHttpRequestException("Bad Request");
        }
    }

    private bool ValidateHash(ActionExecutingContext actionContext)
    {
        actionContext.HttpContext.Request.Body.Position = 0;
        using var stream = new MemoryStream();
        actionContext.HttpContext.Request.Body.CopyToAsync(stream).Wait();
        var requestBody = Encoding.UTF8.GetString(stream.ToArray());
        var svc = actionContext.HttpContext.RequestServices;
        var shopifySettings = svc.GetService<IOptions<ShopifySettings>>()?.Value;
        var keyBytes = Encoding.UTF8.GetBytes(shopifySettings.ApiSecretKey);
        var dataBytes = Encoding.UTF8.GetBytes(requestBody);
        var hmac = new HMACSHA256(keyBytes);
        var hmacBytes = hmac.ComputeHash(dataBytes);
        var hmacHeader = actionContext.HttpContext.Request.Headers["x-shopify-hmac-sha256"];
        var createSignature = Convert.ToBase64String(hmacBytes);
        return hmacHeader == createSignature;
    }
}

并将其添加到您想要验证的控制器的 webhook 方法中:

    [HttpPost("~/order")]
    [VerifyShopify]
    public async Task<IActionResult> Post()
    {
        return Ok();
    }
© www.soinside.com 2019 - 2024. All rights reserved.