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 isSigOksuppose 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.fromappel lancera l'erreur que vous avez citée si signaturec'est undefinedparce que l'en-tête n'est pas là. Vous voudrez probablement revenir falsedans 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 Requsethérite de celui de Node.js IncomingMessage), les noms d'en-tête headerssont 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. timingSafeEquallancera 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