From 6245d1eabd14f9596acee8a63d15e2bd91cc69f5 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Sun, 26 Apr 2026 20:31:06 +0200 Subject: [PATCH] fix(auth): replace fixed-salt bcrypt with HMAC-SHA256 for API key hashing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit bcrypt with a fixed salt is deterministic but defeats the purpose of bcrypt salting. API keys are high-entropy UUIDs (128 bits), making brute-force resistance unnecessary — HMAC-SHA256 with the app secret is the correct primitive for deterministic, secure key hashing. Also fixes application.author.id → application.authorId in the API key auth path, which was silently setting req.payload.id to undefined. --- src/config/passport-headerapikey.js | 22 ++++++++++------------ src/middlewares/auth.js | 14 ++++++-------- 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/src/config/passport-headerapikey.js b/src/config/passport-headerapikey.js index 1e2da7a..e51c6fe 100644 --- a/src/config/passport-headerapikey.js +++ b/src/config/passport-headerapikey.js @@ -1,5 +1,5 @@ -var secret = require('.').secret, - bcrypt = require('bcrypt'); +var secret = require('.').secret; +const crypto = require('crypto'); const passport = require('passport'); var HeaderAPIKeyStrategy = require('passport-headerapikey').HeaderAPIKeyStrategy; const DB = require('../database/mysql'); @@ -8,15 +8,13 @@ passport.use('headerapikey', new HeaderAPIKeyStrategy( { header: 'Authorization', prefix: `${process.env.AUTHORIZATION_PREFIX} ` }, false, function (apikey, done) { - const salt = secret; - Promise.resolve(bcrypt.hash(apikey, salt)).then(function (encryptedKey) { - DB.Application.findOne({ where: { apikey: encryptedKey } }) - .then(function (application) { - if (!application) { - return done({ message: "Application can't be found.", status: 404 }, false, false); - } - return done(null, application); - }); - }).catch(done); + const encryptedKey = crypto.createHmac('sha256', secret).update(apikey).digest('hex'); + DB.Application.findOne({ where: { apikey: encryptedKey } }) + .then(function (application) { + if (!application) { + return done({ message: "Application can't be found.", status: 404 }, false, false); + } + return done(null, application); + }).catch(done); } )); diff --git a/src/middlewares/auth.js b/src/middlewares/auth.js index 0666068..c1e101a 100644 --- a/src/middlewares/auth.js +++ b/src/middlewares/auth.js @@ -2,7 +2,7 @@ //var jwt = require('express-jwt'); var { expressjwt: jwt } = require("express-jwt"), secret = require('../config').secret, - bcrypt = require('bcrypt'); + crypto = require('crypto'); const { v4 } = require('uuid'), passport = require('passport'), auths = ['Api-Key', 'Basic', 'Bearer', 'Token']; @@ -38,7 +38,7 @@ const auth = { return res.status(401).json({message: "Unauthorized - You are not allowed to access this resource.", err: err, info: info}); } req.application = application; - req.payload = {id: application.author.id}; + req.payload = { id: application.authorId }; return next(); })(req, res, next); } else { @@ -57,13 +57,11 @@ const auth = { } return null; }, - generateAPIKey: async () => { + generateAPIKey: () => { const key = v4().replace(/-/g, ''); - const salt = secret; - // Génération d'un salt aléatoire : const salt = await Promise.resolve(bcrypt.genSalt(10)); - const maskedKey = `${key.slice(0, 4)}...${key.slice(-4)}`; - const encryptedKey = await Promise.resolve(bcrypt.hash(key, salt)); - return { key: key, masked: maskedKey, encrypted: encryptedKey}; + const maskedKey = `${key.slice(0, 4)}...${key.slice(-4)}`; + const encryptedKey = crypto.createHmac('sha256', secret).update(key).digest('hex'); + return { key, masked: maskedKey, encrypted: encryptedKey }; }, authenticateKey: async (req, res, next) => { passport.authenticate('headerapikey', { session: false }, function (err, application, info) {