diff --git a/exec/chksetup.js b/exec/chksetup.js
index cf0359e48e2262755190db1064d60d5941d2f99c..f01567027bf80847ef8720b24a1d47dc16ff7b58 100644
--- a/exec/chksetup.js
+++ b/exec/chksetup.js
@@ -154,6 +154,10 @@ var tests = {
 					, usr.number
 					, password.length
 					, system.max_password_length));
+			else if(!system.check_password(password))
+				output.push(format("User #%-4u has a low quality password%s"
+					, usr.number
+					, options.verbose ? (': ' + password) : ''));
 			if(!system.trashcan("password", password))
 				continue;
 			output.push(format("User #%-4u has a disallowed password%s"
diff --git a/src/sbbs3/js_bbs.cpp b/src/sbbs3/js_bbs.cpp
index cece14de9115baf02c7d8d1ca2feddb44c6932ba..a2e6692754e1ab302df3591b52ba73ebce60acaa 100644
--- a/src/sbbs3/js_bbs.cpp
+++ b/src/sbbs3/js_bbs.cpp
@@ -5014,7 +5014,7 @@ static jsSyncMethodSpec js_bbs_functions[] = {
 	{"good_password",   js_chkpass,         1,  JSTYPE_ALIAS },
 	{"check_password",  js_chkpass,         1,  JSTYPE_BOOLEAN, JSDOCSTR("password, [forced_unique=false]")
 	 , JSDOCSTR("Check if requested user password meets minimum password requirements "
-		        "(length, uniqueness, etc.) (AKA good_password).<br>"
+		        "(length, uniqueness, etc.) (AKA good_password). Also checks the <tt>password.can</tt> filter file.<br>"
 		        "When <i>forced_unique</i> is <tt>true</tt>, the password must be substantially different from the user's current password.")
 	 , 310
 	},
diff --git a/src/sbbs3/js_system.c b/src/sbbs3/js_system.c
index fe72db1320f1b6b802399fb98101c79f75e4eac4..24defb3645b1d46c3e675b4538c8a7f1b718a34c 100644
--- a/src/sbbs3/js_system.c
+++ b/src/sbbs3/js_system.c
@@ -2086,8 +2086,7 @@ js_chkpassword(JSContext *cx, uintN argc, jsval *arglist)
 		return JS_FALSE;
 
 	rc = JS_SUSPENDREQUEST(cx);
-	bool result = check_pass(sys->cfg, str, /* user: */NULL, /* unique: */false, /* reason: */NULL)
-		              && !trashcan(sys->cfg, str, "password");
+	bool result = check_pass(sys->cfg, str, /* user: */NULL, /* unique: */false, /* reason: */NULL);
 	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(result));
 	JS_RESUMEREQUEST(cx, rc);
 
@@ -2401,7 +2400,8 @@ static jsSyncMethodSpec js_system_functions[] = {
 	 , 321},
 	{"check_password",  js_chkpassword,     1,  JSTYPE_BOOLEAN, JSDOCSTR("password")
 	 , JSDOCSTR("Check that the provided string is suitable for a new user password, "
-		        "returns <tt>true</tt> if it meets the system criteria for a user password")
+		        "returns <tt>true</tt> if it meets the system criteria for a user password.<br>"
+				"Does <b>not</b> check the <tt>password.can</tt> file.")
 	 , 321},
 	{"check_filename",  js_chkfname,        1,  JSTYPE_BOOLEAN, JSDOCSTR("filename")
 	 , JSDOCSTR("Verify that the specified <i>filename</i> string is legal and allowed for upload by users "
diff --git a/src/sbbs3/newuser.cpp b/src/sbbs3/newuser.cpp
index 57607765a80391e2faf063f40bd7a9f44ce6f88e..ce5d56cf5555640bcfd5bd8646d62b2042092745 100644
--- a/src/sbbs3/newuser.cpp
+++ b/src/sbbs3/newuser.cpp
@@ -363,13 +363,16 @@ bool sbbs_t::newuser()
 			else
 				lprintf(LOG_NOTICE, "Rejected RLogin password for new user");
 		}
-		c = 0;
-		while (c < MAX(RAND_PASS_LEN, cfg.min_pwlen)) {              /* Create random password */
-			useron.pass[c] = sbbs_random(43) + '0';
-			if (IS_ALPHANUMERIC(useron.pass[c]))
-				c++;
-		}
-		useron.pass[c] = 0;
+		lprintf(LOG_INFO, "Generating a random password for new user");
+		do {
+			c = 0;
+			while (c < MAX(RAND_PASS_LEN, cfg.min_pwlen)) {              /* Create random password */
+				useron.pass[c] = sbbs_random(43) + '0';
+				if (IS_ALPHANUMERIC(useron.pass[c]))
+					c++;
+			}
+			useron.pass[c] = 0;
+		} while (!check_pass(&cfg, useron.pass, &useron, /* unique: */false, /* reason: */NULL));
 
 		bprintf(text[YourPasswordIs], useron.pass);
 
diff --git a/src/sbbs3/scfg/scfgsys.c b/src/sbbs3/scfg/scfgsys.c
index 4b366f407709a35fec7c9f8e419dfe463392f985..1cfdfea2686c482fecfb2416ebc6d0bcb4e8c9ad 100644
--- a/src/sbbs3/scfg/scfgsys.c
+++ b/src/sbbs3/scfg/scfgsys.c
@@ -600,7 +600,6 @@ void security_cfg(void)
 		         , (!(cfg.uq & UQ_ALIASES) || cfg.sys_login & LOGIN_REALNAME) ? "Yes" : "No");
 		snprintf(opt[i++], MAX_OPLN, "%-33.33s%s", "Allow Login by User Number"
 		         , (cfg.sys_login & LOGIN_USERNUM) ? "Yes" : "No");
-
 		SAFEPRINTF(str, "%s Password"
 		           , cfg.sys_misc & SM_PWEDIT && cfg.sys_pwdays ? "Users Must Change"
 		    : cfg.sys_pwdays ? "Users Get New Random" : "Users Can Choose");
@@ -613,6 +612,8 @@ void security_cfg(void)
 		if (cfg.sys_misc & SM_PWEDIT)
 			sprintf(tmp + strlen(tmp), ", %u chars minimum", cfg.min_pwlen);
 		snprintf(opt[i++], MAX_OPLN, "%-33.33s%s", str, tmp);
+		snprintf(opt[i++], MAX_OPLN, "%-33.33s%s", "Demand High Quality Password"
+				, cfg.hq_password ? "Yes" : "No");
 		snprintf(opt[i++], MAX_OPLN, "%-33.33s%s", "Always Prompt for Password"
 		         , cfg.sys_login & LOGIN_PWPROMPT ? "Yes":"No");
 		snprintf(opt[i++], MAX_OPLN, "%-33.33s%s", "Display/Log Passwords Locally"
@@ -811,6 +812,21 @@ void security_cfg(void)
 					cfg.sys_pwdays = 0;
 				}
 				break;
+			case __COUNTER__:
+				i = (cfg.hq_password) ? 0:1;
+				uifc.helpbuf =
+					"`Demand High Quality Password:`\n"
+					"\n"
+					"If you want users to be required to have a \"high quality\" password\n"
+					"based on calculated entropy, set this option to `Yes`.\n"
+					"\n"
+					"For elevated security, set this option to `Yes`.\n"
+				;
+				i = uifc.list(WIN_MID | WIN_SAV, 0, 10, 0, &i, 0
+				              , "Require Users to use High Quality/Entropy Passwords", uifcYesNoOpts);
+				if ((i == 0 && !cfg.hq_password) || (i == 1 && cfg.hq_password))
+					cfg.hq_password = !cfg.hq_password;
+				break;
 			case __COUNTER__:
 				i = cfg.sys_login & LOGIN_PWPROMPT ? 0:1;
 				uifc.helpbuf =
diff --git a/src/sbbs3/scfgdefs.h b/src/sbbs3/scfgdefs.h
index 59d342cf422b0cb577887f072538b08b16f17038..e956d6d6375e8658c0de43c6de90ba8cce769dd8 100644
--- a/src/sbbs3/scfgdefs.h
+++ b/src/sbbs3/scfgdefs.h
@@ -486,6 +486,7 @@ typedef struct
 	fevent_t 		sys_daily;			/* Daily event */
 	fevent_t 		sys_logon;			/* Logon event */
 	fevent_t 		sys_logout;			/* Logout event */
+	bool			hq_password;		/* Require high quality/entropy user passwords */
 	uint8_t			min_pwlen;
 	uint16_t		sys_pwdays; 		/* Max days between password change */
 	uint16_t		sys_deldays;		/* Days to keep deleted users */
diff --git a/src/sbbs3/scfglib1.c b/src/sbbs3/scfglib1.c
index 159486fac2a26cddd8e1933e5ea6a1a4ab6d0ea2..d8a247041057b308fb4c736dbfba1c0c77cbe439 100644
--- a/src/sbbs3/scfglib1.c
+++ b/src/sbbs3/scfglib1.c
@@ -127,6 +127,7 @@ bool read_main_cfg(scfg_t* cfg, char* error, size_t maxerrlen)
 	cfg->max_minutes = iniGetInteger(ini, ROOT_SECTION, "max_minutes", 0);
 	cfg->cdt_per_dollar = (uint32_t)iniGetBytes(ini, ROOT_SECTION, "cdt_per_dollar", 1, 0);
 	cfg->guest_msgscan_init = iniGetInteger(ini, ROOT_SECTION, "guest_msgscan_init", 0);
+	cfg->hq_password = iniGetBool(ini, ROOT_SECTION, "hq_password", false);
 	cfg->min_pwlen = iniGetInteger(ini, ROOT_SECTION, "min_password_length", 0);
 	if (cfg->min_pwlen < MIN_PASS_LEN)
 		cfg->min_pwlen = MIN_PASS_LEN;
diff --git a/src/sbbs3/scfgsave.c b/src/sbbs3/scfgsave.c
index 441702bcaf424d68508259b53d1732528c8b03b2..e5b9335d534aa7c17de5cd073fb07688b8565acf 100644
--- a/src/sbbs3/scfgsave.c
+++ b/src/sbbs3/scfgsave.c
@@ -140,6 +140,7 @@ bool write_main_cfg(scfg_t* cfg)
 	iniSetUInteger(&ini, ROOT_SECTION, "cdt_per_dollar", cfg->cdt_per_dollar, NULL);
 	iniSetBool(&ini, ROOT_SECTION, "new_install", cfg->new_install, NULL);
 	iniSetUInteger(&ini, ROOT_SECTION, "guest_msgscan_init", cfg->guest_msgscan_init, NULL);
+	iniSetBool(&ini, ROOT_SECTION, "hq_password", cfg->hq_password, NULL);
 	iniSetUInteger(&ini, ROOT_SECTION, "min_password_length", cfg->min_pwlen, NULL);
 	iniSetBytes(&ini, ROOT_SECTION, "max_log_size", 1, cfg->max_log_size, NULL);
 	iniSetUInteger(&ini, ROOT_SECTION, "max_logs_kept", cfg->max_logs_kept, NULL);
diff --git a/src/sbbs3/userdat.c b/src/sbbs3/userdat.c
index 9b2b0e095512f0909692698f21a43ddfc1be3c0b..52d1b555298c76bb681109e8bd6b670a79c6c518 100644
--- a/src/sbbs3/userdat.c
+++ b/src/sbbs3/userdat.c
@@ -3965,6 +3965,91 @@ bool check_realname(scfg_t* cfg, const char* name)
 	return (uchar)name[0] < 0x7f && name[1] && IS_ALPHA(name[0]) && strchr(name, ' ');
 }
 
+/****************************************************************************/
+/* Check a password for uniqueness and validity								*/
+/* Does *not* check the password.can file!									*/
+/****************************************************************************/
+bool check_pass(scfg_t* cfg, const char *pass, user_t* user, bool unique, int* reason)
+{
+	int reason_;
+
+	if (reason == NULL)
+		reason = &reason_;
+
+	int len = strlen(pass);
+	if (len < cfg->min_pwlen || len < MIN_PASS_LEN) {
+		*reason = PasswordTooShort;
+		return false;
+	}
+	if (unique) {
+		if (user == NULL)
+			return false;
+		if (stricmp(pass, user->pass) == 0) {
+			*reason = PasswordNotChanged;
+			return false;
+		}
+	}
+
+	// Require a minimum number of unique (non-repeating/incrementing/decrementing) characters
+	if (cfg->hq_password) {
+		int i;
+		char good[LEN_PASS + 1] = {0};
+		int g = 0;
+		for (i = 0; i < len; ++i) {
+			if (abs(toupper(pass[i]) - toupper(pass[i + 1])) > 1 && strchr(good, pass[i]) == NULL)
+				good[g++] = pass[i];
+		}
+		if (g < cfg->min_pwlen) {
+			*reason = PasswordInvalid;
+			return false;
+		}
+	}
+
+	// Compare proposed password against user properties
+	if (user != NULL) {
+		char first[128], last[128], *p;
+
+		SAFECOPY(first, user->alias);
+		p = strchr(first, ' ');
+		if (p) {
+			*p = 0;
+			SAFECOPY(last, p + 1);
+		}
+		else
+			last[0] = 0;
+		if ((unique && user->pass[0]
+			 && (strcasestr(pass, user->pass) || strcasestr(user->pass, pass)))
+			|| (user->name[0]
+				&& (strcasestr(pass, user->name) || strcasestr(user->name, pass)))
+			|| strcasestr(pass, user->alias) || strcasestr(user->alias, pass)
+			|| strcasestr(pass, first) || strcasestr(first, pass)
+			|| (last[0]
+				&& (strcasestr(pass, last) || strcasestr(last, pass)))
+			|| strcasestr(pass, user->handle) || strcasestr(user->handle, pass)
+			|| (user->zipcode[0]
+				&& (strcasestr(pass, user->zipcode) || strcasestr(user->zipcode, pass)))
+			|| (user->phone[0] && strcasestr(user->phone, pass))
+			) {
+			*reason = PasswordObvious;
+			return false;
+		}
+	}
+
+	// Compare proposed password against system properties
+	if ((cfg->sys_name[0]
+	        && (strcasestr(pass, cfg->sys_name) || strcasestr(cfg->sys_name, pass)))
+	    || (cfg->sys_op[0]
+	        && (strcasestr(pass, cfg->sys_op) || strcasestr(cfg->sys_op, pass)))
+	    || (cfg->sys_id[0]
+	        && (strcasestr(pass, cfg->sys_id) || strcasestr(cfg->sys_id, pass)))
+	    || (cfg->node_phone[0] && strcasestr(pass, cfg->node_phone))
+	    ) {
+		*reason = PasswordObvious;
+		return false;
+	}
+	return true;
+}
+
 /****************************************************************************/
 /* Login attempt/hack tracking												*/
 /****************************************************************************/
@@ -4588,88 +4673,3 @@ enum parsed_vpath parse_vpath(scfg_t* cfg, const char* vpath, int* lib, int* dir
 
 	return *filename == NULL ? PARSED_VPATH_DIR : PARSED_VPATH_FULL;
 }
-
-/****************************************************************************/
-/* Check a password for uniqueness and validity								*/
-/* Does *not* check the password.can file!									*/
-/****************************************************************************/
-bool check_pass(scfg_t* cfg, const char *pass, user_t* user, bool unique, int* reason)
-{
-	int reason_;
-
-	if (reason == NULL)
-		reason = &reason_;
-
-	int len = strlen(pass);
-	if (len < cfg->min_pwlen || len < MIN_PASS_LEN) {
-		*reason = PasswordTooShort;
-		return false;
-	}
-	if (unique) {
-		if (user == NULL)
-			return false;
-		if (stricmp(pass, user->pass) == 0) {
-			*reason = PasswordNotChanged;
-			return false;
-		}
-	}
-
-	// Require a minimum sequence of unique (non-repeating/increment/decrementing) characters
-	int i;
-	int run = 0;
-	for (i = 0; i < (len - 1); ++i) {
-		if (abs(toupper(pass[i]) - toupper(pass[i + 1])) > 1) {
-			if (++run >= cfg->min_pwlen / 2)
-				break;
-		} else
-			run = 0;
-	}
-	if (i >= (len - 1)) {
-		*reason = PasswordInvalid;
-		return false;
-	}
-
-	// Compare proposed password against user properties
-	if (user != NULL) {
-		char first[128], last[128], *p;
-
-		SAFECOPY(first, user->alias);
-		p = strchr(first, ' ');
-		if (p) {
-			*p = 0;
-			SAFECOPY(last, p + 1);
-		}
-		else
-			last[0] = 0;
-		if ((unique && user->pass[0]
-			 && (strcasestr(pass, user->pass) || strcasestr(user->pass, pass)))
-			|| (user->name[0]
-				&& (strcasestr(pass, user->name) || strcasestr(user->name, pass)))
-			|| strcasestr(pass, user->alias) || strcasestr(user->alias, pass)
-			|| strcasestr(pass, first) || strcasestr(first, pass)
-			|| (last[0]
-				&& (strcasestr(pass, last) || strcasestr(last, pass)))
-			|| strcasestr(pass, user->handle) || strcasestr(user->handle, pass)
-			|| (user->zipcode[0]
-				&& (strcasestr(pass, user->zipcode) || strcasestr(user->zipcode, pass)))
-			|| (user->phone[0] && strcasestr(user->phone, pass))
-			) {
-			*reason = PasswordObvious;
-			return false;
-		}
-	}
-
-	// Compare proposed password against system properties
-	if ((cfg->sys_name[0]
-	        && (strcasestr(pass, cfg->sys_name) || strcasestr(cfg->sys_name, pass)))
-	    || (cfg->sys_op[0]
-	        && (strcasestr(pass, cfg->sys_op) || strcasestr(cfg->sys_op, pass)))
-	    || (cfg->sys_id[0]
-	        && (strcasestr(pass, cfg->sys_id) || strcasestr(cfg->sys_id, pass)))
-	    || (cfg->node_phone[0] && strcasestr(pass, cfg->node_phone))
-	    ) {
-		*reason = PasswordObvious;
-		return false;
-	}
-	return true;
-}