英文:
Shopify HMAC verification failing in webhook implementation
问题
我正在开发一个Shopify应用程序。我试图使用这个函数来验证在应用程序上发生的Shopify交易,使用Shopify的HMAC身份验证验证。Webhook是通过Shopify Webhook API创建的,主题是app_subscriptions/update,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,检查时生成的哈希和hmac不相等。有人可以告诉我这个实现是否有任何问题吗?提前谢谢。
英文:
I'm working on developing a Shopify app. I'm trying to verify the Shopify transaction that happened on the app using Shopify's HMAC authentication verification using this function. The webhook is created through the Shopify Webhook API for the topic app_subscriptions/update and shopifyApiSecret is the Client Secret for our Shopify app. Here's the snippet of the code we're using,
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;
}
}
We've tried comparing both the ways, that is with === as well as using timingSafeEqual, but the function always returns false and the generatedHash and hmac are not equal on inspection. Can anyone let me know if there is anything wrong with this implementation? Thanks in advance.
答案1
得分: 0
以下是我们在.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);
英文:
Here is the implementation we have done in .Net. You can understand the logic from below code:
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);
答案2
得分: 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;
}
}
英文:
The issue was with the data we were consuming to generate the message.
We were able to solve it by using the express function in our router
express.json({
limit: '10mb',
verify: (req, _res, buf) => {
(req as any).rawBody = buf;
},
});
and passing the rawBody to the update function.
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;
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论