mirror of
https://github.com/Ylianst/MeshCentral.git
synced 2024-11-22 22:17:31 +03:00
Added support for banning common passwords.
This commit is contained in:
parent
90c240ae50
commit
5accad39e5
@ -160,7 +160,8 @@
|
|||||||
"reset": { "type": "integer", "description": "Number of days after which the user is required to change the account password." },
|
"reset": { "type": "integer", "description": "Number of days after which the user is required to change the account password." },
|
||||||
"force2factor": { "type": "boolean", "description": "Requires that all accounts setup 2FA." },
|
"force2factor": { "type": "boolean", "description": "Requires that all accounts setup 2FA." },
|
||||||
"skip2factor": { "type": "string", "description": "IP addresses where 2FA login is skipped, for example: 127.0.0.1,192.168.2.0/24" },
|
"skip2factor": { "type": "string", "description": "IP addresses where 2FA login is skipped, for example: 127.0.0.1,192.168.2.0/24" },
|
||||||
"oldPasswordBan": { "type": "integer", "description": "Number of old passwords the server should remember and not allow the user to switch back to." }
|
"oldPasswordBan": { "type": "integer", "description": "Number of old passwords the server should remember and not allow the user to switch back to." },
|
||||||
|
"banCommonPasswords": { "type": "boolean", "description": "Uses WildLeek to block use of the 10000 most commonly used passwords." }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"agentInviteCodes": { "type": "boolean", "default": false, "description": "Enabled a feature where you can set one or more invitation codes in a device group. You can then give a invitation link to users who can use it to download the agent." },
|
"agentInviteCodes": { "type": "boolean", "default": false, "description": "Enabled a feature where you can set one or more invitation codes in a device group. You can then give a invitation link to users who can use it to download the agent." },
|
||||||
|
@ -2646,6 +2646,9 @@ function mainStart() {
|
|||||||
// Lowercase the auth value if present
|
// Lowercase the auth value if present
|
||||||
for (var i in config.domains) { if (typeof config.domains[i].auth == 'string') { config.domains[i].auth = config.domains[i].auth.toLowerCase(); } }
|
for (var i in config.domains) { if (typeof config.domains[i].auth == 'string') { config.domains[i].auth = config.domains[i].auth.toLowerCase(); } }
|
||||||
|
|
||||||
|
// Get the current node version
|
||||||
|
var nodeVersion = Number(process.version.match(/^v(\d+\.\d+)/)[1]);
|
||||||
|
|
||||||
// Check if Windows SSPI, LDAP, Passport and YubiKey OTP will be used
|
// Check if Windows SSPI, LDAP, Passport and YubiKey OTP will be used
|
||||||
var sspi = false;
|
var sspi = false;
|
||||||
var ldap = false;
|
var ldap = false;
|
||||||
@ -2655,6 +2658,7 @@ function mainStart() {
|
|||||||
var mstsc = false;
|
var mstsc = false;
|
||||||
var recordingIndex = false;
|
var recordingIndex = false;
|
||||||
var domainCount = 0;
|
var domainCount = 0;
|
||||||
|
var wildleek = false;
|
||||||
if (require('os').platform() == 'win32') { for (var i in config.domains) { domainCount++; if (config.domains[i].auth == 'sspi') { sspi = true; } else { allsspi = false; } } } else { allsspi = false; }
|
if (require('os').platform() == 'win32') { for (var i in config.domains) { domainCount++; if (config.domains[i].auth == 'sspi') { sspi = true; } else { allsspi = false; } } } else { allsspi = false; }
|
||||||
if (domainCount == 0) { allsspi = false; }
|
if (domainCount == 0) { allsspi = false; }
|
||||||
for (var i in config.domains) {
|
for (var i in config.domains) {
|
||||||
@ -2672,11 +2676,9 @@ function mainStart() {
|
|||||||
if ((typeof config.domains[i].authstrategies.saml == 'object') || (typeof config.domains[i].authstrategies.jumpcloud == 'object')) { passport.push('passport-saml'); }
|
if ((typeof config.domains[i].authstrategies.saml == 'object') || (typeof config.domains[i].authstrategies.jumpcloud == 'object')) { passport.push('passport-saml'); }
|
||||||
}
|
}
|
||||||
if ((config.domains[i].sessionrecording != null) && (config.domains[i].sessionrecording.index == true)) { recordingIndex = true; }
|
if ((config.domains[i].sessionrecording != null) && (config.domains[i].sessionrecording.index == true)) { recordingIndex = true; }
|
||||||
|
if ((config.domains[i].passwordrequirements != null) && (config.domains[i].passwordrequirements.bancommonpasswords == true)) { if (nodeVersion < 8) { config.domains[i].passwordrequirements = false; addServerWarning('Common password checking requires NodeJS v8 or above.'); } else { wildleek = true; } }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the current node version
|
|
||||||
var nodeVersion = Number(process.version.match(/^v(\d+\.\d+)/)[1]);
|
|
||||||
|
|
||||||
// Build the list of required modules
|
// Build the list of required modules
|
||||||
var modules = ['ws', 'cbor', 'nedb', 'https', 'yauzl', 'xmldom', 'ipcheck', 'express', 'archiver@4.0.2', 'multiparty', 'node-forge', 'express-ws', 'compression', 'body-parser', 'connect-redis', 'cookie-session', 'express-handlebars'];
|
var modules = ['ws', 'cbor', 'nedb', 'https', 'yauzl', 'xmldom', 'ipcheck', 'express', 'archiver@4.0.2', 'multiparty', 'node-forge', 'express-ws', 'compression', 'body-parser', 'connect-redis', 'cookie-session', 'express-handlebars'];
|
||||||
if (require('os').platform() == 'win32') { modules.push('node-windows@0.1.14'); if (sspi == true) { modules.push('node-sspi'); } } // Add Windows modules
|
if (require('os').platform() == 'win32') { modules.push('node-windows@0.1.14'); if (sspi == true) { modules.push('node-sspi'); } } // Add Windows modules
|
||||||
@ -2702,6 +2704,9 @@ function mainStart() {
|
|||||||
// Setup encrypted zip support if needed
|
// Setup encrypted zip support if needed
|
||||||
if (config.settings.autobackup && config.settings.autobackup.zippassword) { modules.push('archiver-zip-encrypted'); }
|
if (config.settings.autobackup && config.settings.autobackup.zippassword) { modules.push('archiver-zip-encrypted'); }
|
||||||
|
|
||||||
|
// Setup common password blocking
|
||||||
|
if (wildleek == true) { modules.push('wildleek@2.0.0'); }
|
||||||
|
|
||||||
// Setup 2nd factor authentication
|
// Setup 2nd factor authentication
|
||||||
if (config.settings.no2factorauth !== true) {
|
if (config.settings.no2factorauth !== true) {
|
||||||
// Setup YubiKey OTP if configured
|
// Setup YubiKey OTP if configured
|
||||||
|
@ -2375,9 +2375,12 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
|
|||||||
parent.checkUserPassword(domain, user, command.oldpass, function (result) {
|
parent.checkUserPassword(domain, user, command.oldpass, function (result) {
|
||||||
if (result == true) {
|
if (result == true) {
|
||||||
parent.checkOldUserPasswords(domain, user, command.newpass, function (result) {
|
parent.checkOldUserPasswords(domain, user, command.newpass, function (result) {
|
||||||
if (result == true) {
|
if (result == 1) {
|
||||||
// Send user notification of error
|
// Send user notification of error
|
||||||
displayNotificationMessage('Error, unable to change to previously used password.', 'Account Settings', 'ServerNotify');
|
displayNotificationMessage('Error, unable to change to previously used password.', 'Account Settings', 'ServerNotify');
|
||||||
|
} else if (result == 2) {
|
||||||
|
// Send user notification of error
|
||||||
|
displayNotificationMessage('Error, unable to change to commonly used password.', 'Account Settings', 'ServerNotify');
|
||||||
} else {
|
} else {
|
||||||
// Update the password
|
// Update the password
|
||||||
require('./pass').hash(command.newpass, function (err, salt, hash, tag) {
|
require('./pass').hash(command.newpass, function (err, salt, hash, tag) {
|
||||||
|
@ -136,7 +136,9 @@
|
|||||||
"nonalpha": 1,
|
"nonalpha": 1,
|
||||||
"reset": 90,
|
"reset": 90,
|
||||||
"force2factor": true,
|
"force2factor": true,
|
||||||
"skip2factor": "127.0.0.1,192.168.2.0/24"
|
"skip2factor": "127.0.0.1,192.168.2.0/24",
|
||||||
|
"oldPasswordBan": 5,
|
||||||
|
"banCommonPasswords": false
|
||||||
},
|
},
|
||||||
"_agentInviteCodes": true,
|
"_agentInviteCodes": true,
|
||||||
"_agentNoProxy": true,
|
"_agentNoProxy": true,
|
||||||
|
24
webserver.js
24
webserver.js
@ -1289,7 +1289,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
|||||||
|
|
||||||
// Check if the password is the same as a previous one
|
// Check if the password is the same as a previous one
|
||||||
obj.checkOldUserPasswords(domain, user, req.body.rpassword1, function (result) {
|
obj.checkOldUserPasswords(domain, user, req.body.rpassword1, function (result) {
|
||||||
if (result == true) {
|
if (result != 0) {
|
||||||
// This is the same password as an older one, request a password change again
|
// This is the same password as an older one, request a password change again
|
||||||
parent.debug('web', 'handleResetPasswordRequest: password rejected, use a different one (2)');
|
parent.debug('web', 'handleResetPasswordRequest: password rejected, use a different one (2)');
|
||||||
req.session.loginmode = '6';
|
req.session.loginmode = '6';
|
||||||
@ -1870,6 +1870,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check a user's old passwords
|
// Check a user's old passwords
|
||||||
|
// Callback: 0=OK, 1=OldPass, 2=CommonPass
|
||||||
obj.checkOldUserPasswords = function (domain, user, password, func) {
|
obj.checkOldUserPasswords = function (domain, user, password, func) {
|
||||||
// Check how many old passwords we need to check
|
// Check how many old passwords we need to check
|
||||||
if ((typeof domain.passwordrequirements.oldpasswordban == 'number') && (domain.passwordrequirements.oldpasswordban > 0)) {
|
if ((typeof domain.passwordrequirements.oldpasswordban == 'number') && (domain.passwordrequirements.oldpasswordban > 0)) {
|
||||||
@ -1884,11 +1885,21 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
|||||||
// If there is no old passwords, exit now.
|
// If there is no old passwords, exit now.
|
||||||
var oldPassCount = 1;
|
var oldPassCount = 1;
|
||||||
if (user.oldpasswords != null) { oldPassCount += user.oldpasswords.length; }
|
if (user.oldpasswords != null) { oldPassCount += user.oldpasswords.length; }
|
||||||
var oldPassCheckState = { response: false, count: oldPassCount, user: user, func: func };
|
var oldPassCheckState = { response: 0, count: oldPassCount, user: user, func: func };
|
||||||
|
|
||||||
|
// Test against common passwords if this feature is enabled
|
||||||
|
// Example of common passwords: 123456789, password123
|
||||||
|
if ((domain.passwordrequirements != null) && (domain.passwordrequirements.bancommonpasswords == true)) {
|
||||||
|
oldPassCheckState.count++;
|
||||||
|
require('wildleek')(password).then(function (wild) {
|
||||||
|
if (wild == true) { oldPassCheckState.response = 2; }
|
||||||
|
if (--oldPassCheckState.count == 0) { oldPassCheckState.func(oldPassCheckState.response); }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Try current password
|
// Try current password
|
||||||
require('./pass').hash(password, user.salt, function oldPassCheck(err, hash, tag) {
|
require('./pass').hash(password, user.salt, function oldPassCheck(err, hash, tag) {
|
||||||
if ((err == null) && (hash == tag.user.hash)) { tag.response = true; }
|
if ((err == null) && (hash == tag.user.hash)) { tag.response = 1; }
|
||||||
if (--tag.count == 0) { tag.func(tag.response); }
|
if (--tag.count == 0) { tag.func(tag.response); }
|
||||||
}, oldPassCheckState);
|
}, oldPassCheckState);
|
||||||
|
|
||||||
@ -1898,7 +1909,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
|||||||
const oldpassword = user.oldpasswords[i];
|
const oldpassword = user.oldpasswords[i];
|
||||||
// Default strong password hashing (pbkdf2 SHA384)
|
// Default strong password hashing (pbkdf2 SHA384)
|
||||||
require('./pass').hash(password, oldpassword.salt, function oldPassCheck(err, hash, tag) {
|
require('./pass').hash(password, oldpassword.salt, function oldPassCheck(err, hash, tag) {
|
||||||
if ((err == null) && (hash == tag.oldPassword.hash)) { tag.state.response = true; }
|
if ((err == null) && (hash == tag.oldPassword.hash)) { tag.state.response = 1; }
|
||||||
if (--tag.state.count == 0) { tag.state.func(tag.state.response); }
|
if (--tag.state.count == 0) { tag.state.func(tag.state.response); }
|
||||||
}, { oldPassword: oldpassword, state: oldPassCheckState });
|
}, { oldPassword: oldpassword, state: oldPassCheckState });
|
||||||
}
|
}
|
||||||
@ -1939,9 +1950,12 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
|
|||||||
if (result == true) {
|
if (result == true) {
|
||||||
// Check if the new password is allowed, only do this if this feature is enabled.
|
// Check if the new password is allowed, only do this if this feature is enabled.
|
||||||
parent.checkOldUserPasswords(domain, user, command.newpass, function (result) {
|
parent.checkOldUserPasswords(domain, user, command.newpass, function (result) {
|
||||||
if (result == true) {
|
if (result == 1) {
|
||||||
parent.debug('web', 'handlePasswordChangeRequest: old password reuse attempt.');
|
parent.debug('web', 'handlePasswordChangeRequest: old password reuse attempt.');
|
||||||
if (direct === true) { handleRootRequestEx(req, res, domain); } else { res.redirect(domain.url + getQueryPortion(req)); }
|
if (direct === true) { handleRootRequestEx(req, res, domain); } else { res.redirect(domain.url + getQueryPortion(req)); }
|
||||||
|
} else if (result == 2) {
|
||||||
|
parent.debug('web', 'handlePasswordChangeRequest: commonly used password use attempt.');
|
||||||
|
if (direct === true) { handleRootRequestEx(req, res, domain); } else { res.redirect(domain.url + getQueryPortion(req)); }
|
||||||
} else {
|
} else {
|
||||||
// Update the password
|
// Update the password
|
||||||
require('./pass').hash(req.body.apassword1, function (err, salt, hash, tag) {
|
require('./pass').hash(req.body.apassword1, function (err, salt, hash, tag) {
|
||||||
|
Loading…
Reference in New Issue
Block a user