fix(auth): replace fixed-salt bcrypt with HMAC-SHA256 for API key hashing
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.
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
var secret = require('.').secret,
|
var secret = require('.').secret;
|
||||||
bcrypt = require('bcrypt');
|
const crypto = require('crypto');
|
||||||
const passport = require('passport');
|
const passport = require('passport');
|
||||||
var HeaderAPIKeyStrategy = require('passport-headerapikey').HeaderAPIKeyStrategy;
|
var HeaderAPIKeyStrategy = require('passport-headerapikey').HeaderAPIKeyStrategy;
|
||||||
const DB = require('../database/mysql');
|
const DB = require('../database/mysql');
|
||||||
@@ -8,15 +8,13 @@ passport.use('headerapikey', new HeaderAPIKeyStrategy(
|
|||||||
{ header: 'Authorization', prefix: `${process.env.AUTHORIZATION_PREFIX} ` },
|
{ header: 'Authorization', prefix: `${process.env.AUTHORIZATION_PREFIX} ` },
|
||||||
false,
|
false,
|
||||||
function (apikey, done) {
|
function (apikey, done) {
|
||||||
const salt = secret;
|
const encryptedKey = crypto.createHmac('sha256', secret).update(apikey).digest('hex');
|
||||||
Promise.resolve(bcrypt.hash(apikey, salt)).then(function (encryptedKey) {
|
|
||||||
DB.Application.findOne({ where: { apikey: encryptedKey } })
|
DB.Application.findOne({ where: { apikey: encryptedKey } })
|
||||||
.then(function (application) {
|
.then(function (application) {
|
||||||
if (!application) {
|
if (!application) {
|
||||||
return done({ message: "Application can't be found.", status: 404 }, false, false);
|
return done({ message: "Application can't be found.", status: 404 }, false, false);
|
||||||
}
|
}
|
||||||
return done(null, application);
|
return done(null, application);
|
||||||
});
|
|
||||||
}).catch(done);
|
}).catch(done);
|
||||||
}
|
}
|
||||||
));
|
));
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
//var jwt = require('express-jwt');
|
//var jwt = require('express-jwt');
|
||||||
var { expressjwt: jwt } = require("express-jwt"),
|
var { expressjwt: jwt } = require("express-jwt"),
|
||||||
secret = require('../config').secret,
|
secret = require('../config').secret,
|
||||||
bcrypt = require('bcrypt');
|
crypto = require('crypto');
|
||||||
const { v4 } = require('uuid'),
|
const { v4 } = require('uuid'),
|
||||||
passport = require('passport'),
|
passport = require('passport'),
|
||||||
auths = ['Api-Key', 'Basic', 'Bearer', 'Token'];
|
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});
|
return res.status(401).json({message: "Unauthorized - You are not allowed to access this resource.", err: err, info: info});
|
||||||
}
|
}
|
||||||
req.application = application;
|
req.application = application;
|
||||||
req.payload = {id: application.author.id};
|
req.payload = { id: application.authorId };
|
||||||
return next();
|
return next();
|
||||||
})(req, res, next);
|
})(req, res, next);
|
||||||
} else {
|
} else {
|
||||||
@@ -57,13 +57,11 @@ const auth = {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
generateAPIKey: async () => {
|
generateAPIKey: () => {
|
||||||
const key = v4().replace(/-/g, '');
|
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 maskedKey = `${key.slice(0, 4)}...${key.slice(-4)}`;
|
||||||
const encryptedKey = await Promise.resolve(bcrypt.hash(key, salt));
|
const encryptedKey = crypto.createHmac('sha256', secret).update(key).digest('hex');
|
||||||
return { key: key, masked: maskedKey, encrypted: encryptedKey};
|
return { key, masked: maskedKey, encrypted: encryptedKey };
|
||||||
},
|
},
|
||||||
authenticateKey: async (req, res, next) => {
|
authenticateKey: async (req, res, next) => {
|
||||||
passport.authenticate('headerapikey', { session: false }, function (err, application, info) {
|
passport.authenticate('headerapikey', { session: false }, function (err, application, info) {
|
||||||
|
|||||||
Reference in New Issue
Block a user