From eac092e661bbca94cdbb8fc04a251a3df47f0339 Mon Sep 17 00:00:00 2001 From: Pavel Djundik Date: Fri, 5 Jan 2018 15:26:12 +0200 Subject: [PATCH] Hash user tokens, increase token entropy Fixes #1934 --- src/client.js | 27 ++++++++++++++++++++++++++- src/server.js | 24 +++++++++++++++--------- 2 files changed, 41 insertions(+), 10 deletions(-) diff --git a/src/client.js b/src/client.js index eda7f194..e3185a62 100644 --- a/src/client.js +++ b/src/client.js @@ -94,6 +94,27 @@ function Client(manager, name, config) { if (typeof client.config.sessions !== "object") { client.config.sessions = {}; + } else { + // TODO: This is just for backwards compatibility. Remove in v3.0.0 + const newSessions = {}; + let changed = false; + + _.forOwn(client.config.sessions, (session, key) => { + if (key.length !== 128) { + key = client.calculateTokenHash(key); + changed = true; + } + + newSessions[key] = session; + }); + + if (changed) { + log.info(`User ${colors.bold(client.name)} has been updated with new security requirements for tokens.`); + + delete client.config.token; + client.config.sessions = newSessions; + client.save(); + } } _.forOwn(client.config.sessions, (session) => { @@ -282,7 +303,7 @@ Client.prototype.connect = function(args) { }; Client.prototype.generateToken = function(callback) { - crypto.randomBytes(48, (err, buf) => { + crypto.randomBytes(64, (err, buf) => { if (err) { throw err; } @@ -291,6 +312,10 @@ Client.prototype.generateToken = function(callback) { }); }; +Client.prototype.calculateTokenHash = function(token) { + return crypto.createHash("sha512").update(token).digest("hex"); +}; + Client.prototype.updateSession = function(token, ip, request) { const client = this; const agent = UAParser(request.headers["user-agent"] || ""); diff --git a/src/server.js b/src/server.js index 6ec523ff..e2006004 100644 --- a/src/server.js +++ b/src/server.js @@ -373,7 +373,7 @@ function initializeClient(socket, client, token, lastMessage) { }); socket.on("push:register", (subscription) => { - if (!client.isRegistered() || !client.config.sessions[token]) { + if (!client.isRegistered() || !client.config.sessions.hasOwnProperty(token)) { return; } @@ -418,7 +418,7 @@ function initializeClient(socket, client, token, lastMessage) { tokenToSignOut = token; } - if (!(tokenToSignOut in client.config.sessions)) { + if (!client.config.sessions.hasOwnProperty(tokenToSignOut)) { return; } @@ -459,11 +459,11 @@ function initializeClient(socket, client, token, lastMessage) { if (!Helper.config.public && token === null) { client.generateToken((newToken) => { - client.attachedClients[socket.id].token = token = newToken; + client.attachedClients[socket.id].token = token = client.calculateTokenHash(newToken); client.updateSession(token, getClientIp(socket), socket.request); - sendInitEvent(token); + sendInitEvent(newToken); }); } else { sendInitEvent(null); @@ -495,8 +495,9 @@ function getClientConfiguration() { function performAuthentication(data) { const socket = this; let client; + let token = null; - const finalInit = () => initializeClient(socket, client, data.token || null, data.lastMessage || -1); + const finalInit = () => initializeClient(socket, client, token, data.lastMessage || -1); const initClient = () => { socket.emit("configuration", getClientConfiguration()); @@ -548,11 +549,16 @@ function performAuthentication(data) { client = manager.findClient(data.user); // We have found an existing user and client has provided a token - if (client && data.token && typeof client.config.sessions[data.token] !== "undefined") { - client.updateSession(data.token, getClientIp(socket), socket.request); + if (client && data.token) { + const providedToken = client.calculateTokenHash(data.token); - authCallback(true); - return; + if (client.config.sessions.hasOwnProperty(providedToken)) { + token = providedToken; + + client.updateSession(providedToken, getClientIp(socket), socket.request); + + return authCallback(true); + } } // Perform password checking