Add configurable password strength check on database password (#9782)

* Set default value of DatabasePasswordMinimumQuality to 3 (do not accept a master password that is less than Good)

* Add custom message box button "Continue with weak password"
This commit is contained in:
egglessness 2024-01-06 19:53:18 +01:00 committed by GitHub
parent b2e6dc5fda
commit d44486ce94
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 67 additions and 1 deletions

View File

@ -1843,6 +1843,18 @@ Are you sure you want to continue without a password?</source>
<source>Failed to change database credentials</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Weak password</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>You must enter a stronger password to protect your database.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>This is a weak password! For better protection of your secrets, you should choose a stronger password.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>DatabaseSettingsWidgetEncryption</name>
@ -6511,6 +6523,10 @@ Do you want to overwrite it?</source>
<source>Continue</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Continue with weak password</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>QObject</name>

View File

@ -144,6 +144,7 @@ static const QHash<Config::ConfigKey, ConfigDirective> configStrings = {
{Config::Security_NoConfirmMoveEntryToRecycleBin,{QS("Security/NoConfirmMoveEntryToRecycleBin"), Roaming, true}},
{Config::Security_EnableCopyOnDoubleClick,{QS("Security/EnableCopyOnDoubleClick"), Roaming, false}},
{Config::Security_QuickUnlock, {QS("Security/QuickUnlock"), Local, true}},
{Config::Security_DatabasePasswordMinimumQuality, {QS("Security/DatabasePasswordMinimumQuality"), Local, 0}},
// Browser
{Config::Browser_Enabled, {QS("Browser/Enabled"), Roaming, false}},

View File

@ -124,6 +124,7 @@ public:
Security_NoConfirmMoveEntryToRecycleBin,
Security_EnableCopyOnDoubleClick,
Security_QuickUnlock,
Security_DatabasePasswordMinimumQuality,
Browser_Enabled,
Browser_ShowNotification,

View File

@ -66,6 +66,7 @@ void MessageBox::initializeButtonDefs()
{Disable, {QMessageBox::tr("Disable"), QMessageBox::ButtonRole::AcceptRole}},
{Merge, {QMessageBox::tr("Merge"), QMessageBox::ButtonRole::AcceptRole}},
{Continue, {QMessageBox::tr("Continue"), QMessageBox::ButtonRole::AcceptRole}},
{ContinueWithWeakPass, {QMessageBox::tr("Continue with weak password"), QMessageBox::ButtonRole::AcceptRole}},
};
}

View File

@ -58,10 +58,11 @@ public:
Disable = 1 << 25,
Merge = 1 << 26,
Continue = 1 << 27,
ContinueWithWeakPass = 1 << 28,
// Internal loop markers. Update Last when new KeePassXC button is added
First = Ok,
Last = Continue,
Last = ContinueWithWeakPass,
};
enum Action

View File

@ -62,6 +62,14 @@ bool PasswordEditWidget::isEmpty() const
return (visiblePage() == Page::Edit) && m_compUi->enterPasswordEdit->text().isEmpty();
}
PasswordHealth::Quality PasswordEditWidget::getPasswordQuality() const
{
QString pwd = m_compUi->enterPasswordEdit->text();
PasswordHealth passwordHealth(pwd);
return passwordHealth.quality();
}
QWidget* PasswordEditWidget::componentEditWidget()
{
m_compEditWidget = new QWidget();

View File

@ -20,6 +20,8 @@
#include "KeyComponentWidget.h"
#include "core/PasswordHealth.h"
namespace Ui
{
class PasswordEditWidget;
@ -38,6 +40,7 @@ public:
void setPasswordVisible(bool visible);
bool isPasswordVisible() const;
bool isEmpty() const;
PasswordHealth::Quality getPasswordQuality() const;
bool validate(QString& errorMessage) const override;
protected:

View File

@ -17,7 +17,9 @@
#include "DatabaseSettingsWidgetDatabaseKey.h"
#include "core/Config.h"
#include "core/Database.h"
#include "core/PasswordHealth.h"
#include "gui/MessageBox.h"
#include "gui/databasekey/KeyFileEditWidget.h"
#include "gui/databasekey/PasswordEditWidget.h"
@ -153,6 +155,7 @@ bool DatabaseSettingsWidgetDatabaseKey::save()
}
}
// Show warning if database password has not been set
if (m_passwordEditWidget->visiblePage() == KeyComponentWidget::Page::AddNew || m_passwordEditWidget->isEmpty()) {
QScopedPointer<QMessageBox> msgBox(new QMessageBox(this));
msgBox->setIcon(QMessageBox::Warning);
@ -171,6 +174,33 @@ bool DatabaseSettingsWidgetDatabaseKey::save()
return false;
}
// Show warning if database password is weak
if (!m_passwordEditWidget->isEmpty()
&& m_passwordEditWidget->getPasswordQuality() < PasswordHealth::Quality::Good) {
auto dialogResult = MessageBox::warning(this,
tr("Weak password"),
tr("This is a weak password! For better protection of your secrets, "
"you should choose a stronger password."),
MessageBox::ContinueWithWeakPass | MessageBox::Cancel,
MessageBox::Cancel);
if (dialogResult == MessageBox::Cancel) {
return false;
}
}
// If enforced in the config file, deny users from continuing with a weak password
auto minQuality =
static_cast<PasswordHealth::Quality>(config()->get(Config::Security_DatabasePasswordMinimumQuality).toInt());
if (!m_passwordEditWidget->isEmpty() && m_passwordEditWidget->getPasswordQuality() < minQuality) {
MessageBox::critical(this,
tr("Weak password"),
tr("You must enter a stronger password to protect your database."),
MessageBox::Ok,
MessageBox::Ok);
return false;
}
if (!addToCompositeKey(m_keyFileEditWidget, newKey, oldFileKey)) {
return false;
}

View File

@ -286,7 +286,10 @@ void TestGui::testCreateDatabase()
tmpFile.close();
fileDialog()->setNextFileName(tmpFile.fileName());
// click Continue on the warning due to weak password
MessageBox::setNextAnswer(MessageBox::ContinueWithWeakPass);
QTest::keyClick(fileEdit, Qt::Key::Key_Enter);
tmpFile.remove(););
triggerAction("actionDatabaseNew");

View File

@ -1879,6 +1879,8 @@ bool TestGuiFdoSecrets::driveNewDatabaseWizard()
tmpFile.close();
fileDialog()->setNextFileName(tmpFile.fileName());
// click Continue on the warning due to weak password
MessageBox::setNextAnswer(MessageBox::ContinueWithWeakPass);
wizard->accept();
tmpFile.remove();