Comment utiliser Buffer.from() avec crypto.timingSafeEqual() ?
Pour une raison quelconque, je reçois
TypeError [ERR_INVALID_ARG_TYPE]: The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object. Received undefined
des deux arguments à crypto.timingSafeEqual(a, b)
.
j'ai aussi essayé
const a = Buffer.from(signature, 'utf8').toString('base64');
const b = Buffer.from(expectedSignature, 'utf8').toString('base64');
et j'obtiens la même erreur.
Question
Quelqu'un peut-il comprendre pourquoi les arguments ne sont pas des tampons ?
const express = require("express");
const bodyParser = require("body-parser");
const crypto = require('crypto');
const secret = "x";
const app = express();
const PORT = 8080;
app.use(bodyParser.json());
function isSigOk(request, secret) {
// calculate the signature
const expectedSignature = "sha256=" +
crypto.createHmac("sha256", secret)
.update(JSON.stringify(request.body))
.digest("hex");
// compare the signature against the one in the request
const signature = request.headers["X-Hub-Signature-256"];
const a = Buffer.from(signature);
const b = Buffer.from(expectedSignature);
return crypto.timingSafeEqual(a, b);
};
app.post("/", (req, res) => {
if (isSigOk(req, secret)) {
// Do stuff here
} else {
console.log('Error: Signatures does not match. Return res.status(401)');
};
res.status(200).end();
});
// Start express on the defined port
app.listen(PORT, () => console.log(`Github wekhook listening on port ${PORT}`));
Solution du problème
Je vois deux problèmes:
Le premier et le principal est qu'il isSigOk
suppose qu'il y aura une valeur pour l' "X-Hub-Signature-256"
en-tête :
const signature = request.headers["X-Hub-Signature-256"];
const a = Buffer.from(signature);
Cet Buffer.from
appel lancera l'erreur que vous avez citée si signature
c'est undefined
parce que l'en-tête n'est pas là. Vous voudrez probablement revenir false
dans ce cas (et probablement éviter la surcharge de travail sur la signature attendue en réorganisant un peu les choses), voir les ***
commentaires et les lignes associées :
function isSigOk(request, secret) {
// *** get the signature on this message, if any
const signature = request.headers["X-Hub-Signature-256"];
if (!signature) {
// *** none
return false;
}
// calculate the signature
const expectedSignature = "sha256=" +
crypto.createHmac("sha256", secret)
.update(JSON.stringify(request.body))
.digest("hex");
// compare the signature against the one in the request
const a = Buffer.from(signature);
const b = Buffer.from(expectedSignature);
return crypto.timingSafeEqual(a, b);
};
La capitalisation compte. Selon la documentation de Node.js (l'objet d'Express Requset
hérite de celui de Node.js IncomingMessage
), les noms d'en-tête headers
sont en minuscules. Alors request.headers["X-Hub-Signature-256"]
devrait être request.headers["x-hub-signature-256"]
. (Dans un commentaire, vous avez dit que vous obteniez une valeur, mais le commentaire utilisait toutes les minuscules, alors que le code utilise une casse mixte.) Donc :
function isSigOk(request, secret) {
// *** get the signature on this message, if any
const signature = request.headers["x-hub-signature-256"]; // *** Lowercase
if (!signature) {
// *** none
return false;
}
// calculate the signature
const expectedSignature = "sha256=" +
crypto.createHmac("sha256", secret)
.update(JSON.stringify(request.body))
.digest("hex");
// compare the signature against the one in the request
const a = Buffer.from(signature);
const b = Buffer.from(expectedSignature);
return a.length === b.length && crypto.timingSafeEqual(a, b);
};
Notez la a.length === b.length &&
partie de cela. timingSafeEqual
lancera une erreur si les tampons n'ont pas la même longueur, mais nous voulons plutôt retourner false dans cette situation.
Commentaires
Enregistrer un commentaire