From a14bc06ed6412be21b36f30a2139945c4fb792fe Mon Sep 17 00:00:00 2001 From: Gary Allan Date: Mon, 9 Aug 2021 14:23:11 +0100 Subject: [PATCH] Bugfix: CSRF outlined in CVE-2020-7988 (#3373) Update app/tools/pass-change - Add CSRF cookie. - Require old password. - Prevent old password re-use. - Enforce password complexity requirements. Fixes #3373 --- app/tools/pass-change/form.php | 16 +++++++++++++--- app/tools/pass-change/result.php | 28 ++++++++++++++++++++++------ functions/version.php | 2 +- js/magic.js | 9 ++------- misc/CHANGELOG | 2 ++ 5 files changed, 40 insertions(+), 17 deletions(-) diff --git a/app/tools/pass-change/form.php b/app/tools/pass-change/form.php index c79a588fc..838d88548 100755 --- a/app/tools/pass-change/form.php +++ b/app/tools/pass-change/form.php @@ -1,6 +1,9 @@ check_user_session (); + +# create csrf token +$csrf = $User->Crypto->csrf_cookie ("create", "pass-change"); ?>
@@ -20,15 +23,22 @@
+ - -
+ +
+ +
+ + +
+
-
+
diff --git a/app/tools/pass-change/result.php b/app/tools/pass-change/result.php index df9ef800b..3164d9cf5 100755 --- a/app/tools/pass-change/result.php +++ b/app/tools/pass-change/result.php @@ -4,17 +4,33 @@ require_once('../../../functions/functions.php'); # Classes -$Database = new Database_PDO; -$User = new User ($Database); +$Database = new Database_PDO; +$User = new User ($Database); $Result = new Result; +$Password_check = new Password_check (); # user must be authenticated $User->check_user_session (); -# checks -if(strlen($_POST['ipampassword1'])<8) { $Result->show("danger", _("Invalid password"), true); } -if($_POST['ipampassword1']!=$_POST['ipampassword2']) { $Result->show("danger", _("Passwords do not match"), true); } +# Ensure keys exist (php8.0) +$_POST = array_merge(array_fill_keys(['csrf_cookie', 'oldpassword', 'ipampassword1', 'ipampassword2'], null), $_POST); + + +#CSRF +$User->Crypto->csrf_cookie ("validate", "pass-change", $_POST['csrf_cookie']) === false ? $Result->show("danger", _("Invalid CSRF cookie"), true) : ""; + +# Check old password +if(!hash_equals($User->user->password, crypt($_POST['oldpassword'], $User->user->password))) { $Result->show("danger", _("Invalid password"), true); } + +# Check new password != old password +if($_POST['ipampassword1']==$_POST['oldpassword']) { $Result->show("danger", _("New password must be different"), true); } + +# Enforce password policy +$policy = (json_decode($User->settings->passwordPolicy, true)); +$Password_check->set_requirements($policy, explode(",",$policy['allowedSymbols'])); +if (!$Password_check->validate ($_POST['ipampassword1'])) { $Result->show("danger alert-danger ", _('Password validation errors').":
- ".implode("
- ", $Password_check->get_errors ()), true); } + +if($_POST['ipampassword1']!=$_POST['ipampassword2']) { $Result->show("danger", _("New passwords do not match"), true); } # update pass $User->update_user_pass($_POST['ipampassword1']); -?> \ No newline at end of file diff --git a/functions/version.php b/functions/version.php index f19e3b1f6..26e08b00a 100644 --- a/functions/version.php +++ b/functions/version.php @@ -4,7 +4,7 @@ /* set latest version */ define("VERSION_VISIBLE", "1.4.4"); //visible version in footer e.g 1.3.2 /* set latest revision */ -define("REVISION", "028"); //increment on static content changes (js/css) or point releases to avoid caching issues +define("REVISION", "029"); //increment on static content changes (js/css) or point releases to avoid caching issues /* set last possible upgrade */ define("LAST_POSSIBLE", "1.19"); //minimum required version to be able to upgrade /* set published - hide dbversion in footer */ diff --git a/js/magic.js b/js/magic.js index df01fab42..f5ca82c39 100755 --- a/js/magic.js +++ b/js/magic.js @@ -1183,13 +1183,8 @@ $('form#cform').submit(function () { /* changePassRequired */ $('form#changePassRequiredForm').submit(function() { showSpinner(); - - //get username - var ipampassword1 = $('#ipampassword1', this).val(); - var ipampassword2 = $('#ipampassword2', this).val(); - //get login data - var postData = "ipampassword1="+ipampassword1+"&ipampassword2="+ipampassword2; - + //get csrf_cookie, old + new passwords + var postData = $('form#changePassRequiredForm').serialize(); $.post('app/tools/pass-change/result.php', postData, function(data) { $('div#changePassRequiredResult').html(data).fadeIn('fast'); hideSpinner(); diff --git a/misc/CHANGELOG b/misc/CHANGELOG index 3d394c5b6..954353f91 100755 --- a/misc/CHANGELOG +++ b/misc/CHANGELOG @@ -1,4 +1,5 @@ == 1.4.4 + Bugfixes: ---------------------------- + Allow UTF-8 in instruction widgets (#3360); @@ -7,6 +8,7 @@ Security Fixes: ---------------------------- + XSS (reflected) in IP calculator (#3351); + + XSS in pass-change/result.php (#3373); == 1.4.3