diff --git a/src/base/preferences.cpp b/src/base/preferences.cpp index 0f6a458de..ae044fdf2 100644 --- a/src/base/preferences.cpp +++ b/src/base/preferences.cpp @@ -586,6 +586,16 @@ void Preferences::setWebUiClickjackingProtectionEnabled(bool enabled) setValue("Preferences/WebUI/ClickjackingProtection", enabled); } +bool Preferences::isWebUiCSRFProtectionEnabled() const +{ + return value("Preferences/WebUI/CSRFProtection", true).toBool(); +} + +void Preferences::setWebUiCSRFProtectionEnabled(bool enabled) +{ + setValue("Preferences/WebUI/CSRFProtection", enabled); +} + bool Preferences::isWebUiHttpsEnabled() const { return value("Preferences/WebUI/HTTPS/Enabled", false).toBool(); diff --git a/src/base/preferences.h b/src/base/preferences.h index 46616591d..a3b5a2ae7 100644 --- a/src/base/preferences.h +++ b/src/base/preferences.h @@ -197,6 +197,8 @@ public: // WebUI security bool isWebUiClickjackingProtectionEnabled() const; void setWebUiClickjackingProtectionEnabled(bool enabled); + bool isWebUiCSRFProtectionEnabled() const; + void setWebUiCSRFProtectionEnabled(bool enabled); // HTTPS bool isWebUiHttpsEnabled() const; diff --git a/src/gui/optionsdlg.cpp b/src/gui/optionsdlg.cpp index 23af0a055..d0ac39d87 100644 --- a/src/gui/optionsdlg.cpp +++ b/src/gui/optionsdlg.cpp @@ -379,6 +379,7 @@ OptionsDialog::OptionsDialog(QWidget *parent) connect(m_ui->checkBypassAuthSubnetWhitelist, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkBypassAuthSubnetWhitelist, &QAbstractButton::toggled, m_ui->IPSubnetWhitelistButton, &QPushButton::setEnabled); connect(m_ui->checkClickjacking, &QCheckBox::toggled, this, &ThisType::enableApplyButton); + connect(m_ui->checkCSRFProtection, &QCheckBox::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkDynDNS, &QGroupBox::toggled, this, &ThisType::enableApplyButton); connect(m_ui->comboDNSService, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton); connect(m_ui->domainNameTxt, &QLineEdit::textChanged, this, &ThisType::enableApplyButton); @@ -697,6 +698,7 @@ void OptionsDialog::saveOptions() pref->setWebUiAuthSubnetWhitelistEnabled(m_ui->checkBypassAuthSubnetWhitelist->isChecked()); // Security pref->setWebUiClickjackingProtectionEnabled(m_ui->checkClickjacking->isChecked()); + pref->setWebUiCSRFProtectionEnabled(m_ui->checkCSRFProtection->isChecked()); // DynDNS pref->setDynDNSEnabled(m_ui->checkDynDNS->isChecked()); pref->setDynDNSService(m_ui->comboDNSService->currentIndex()); @@ -1101,6 +1103,7 @@ void OptionsDialog::loadOptions() // Security m_ui->checkClickjacking->setChecked(pref->isWebUiClickjackingProtectionEnabled()); + m_ui->checkCSRFProtection->setChecked(pref->isWebUiCSRFProtectionEnabled()); m_ui->checkDynDNS->setChecked(pref->isDynDNSEnabled()); m_ui->comboDNSService->setCurrentIndex(static_cast(pref->getDynDNSService())); diff --git a/src/gui/optionsdlg.ui b/src/gui/optionsdlg.ui index f298fbb62..0393990f8 100644 --- a/src/gui/optionsdlg.ui +++ b/src/gui/optionsdlg.ui @@ -3175,6 +3175,13 @@ Use ';' to split multiple entries. Can use wildcard '*'. + + + + Enable Cross-Site Request Forgery (CSRF) protection + + + diff --git a/src/webui/api/appcontroller.cpp b/src/webui/api/appcontroller.cpp index a970cd06d..fad7bc23e 100644 --- a/src/webui/api/appcontroller.cpp +++ b/src/webui/api/appcontroller.cpp @@ -207,6 +207,7 @@ void AppController::preferencesAction() data["bypass_auth_subnet_whitelist"] = authSubnetWhitelistStringList.join("\n"); // Security data["web_ui_clickjacking_protection_enabled"] = pref->isWebUiClickjackingProtectionEnabled(); + data["web_ui_csrf_protection_enabled"] = pref->isWebUiCSRFProtectionEnabled(); // Update my dynamic domain name data["dyndns_enabled"] = pref->isDynDNSEnabled(); data["dyndns_service"] = pref->getDynDNSService(); @@ -484,6 +485,8 @@ void AppController::setPreferencesAction() // Security if (m.contains("web_ui_clickjacking_protection_enabled")) pref->setWebUiClickjackingProtectionEnabled(m["web_ui_clickjacking_protection_enabled"].toBool()); + if (m.contains("web_ui_csrf_protection_enabled")) + pref->setWebUiCSRFProtectionEnabled(m["web_ui_csrf_protection_enabled"].toBool()); // Update my dynamic domain name if (m.contains("dyndns_enabled")) pref->setDynDNSEnabled(m["dyndns_enabled"].toBool()); diff --git a/src/webui/webapplication.cpp b/src/webui/webapplication.cpp index 3ce44da84..0b98986ba 100644 --- a/src/webui/webapplication.cpp +++ b/src/webui/webapplication.cpp @@ -430,6 +430,7 @@ void WebApplication::configure() } m_isClickjackingProtectionEnabled = pref->isWebUiClickjackingProtectionEnabled(); + m_isCSRFProtectionEnabled = pref->isWebUiCSRFProtectionEnabled(); } void WebApplication::registerAPIController(const QString &scope, APIController *controller) @@ -514,9 +515,11 @@ Http::Response WebApplication::processRequest(const Http::Request &request, cons clear(); try { - // block cross-site requests - if (isCrossSiteRequest(m_request) || !validateHostHeader(m_domainList)) + // block suspicious requests + if ((m_isCSRFProtectionEnabled && isCrossSiteRequest(m_request)) + || !validateHostHeader(m_domainList)) { throw UnauthorizedHTTPError(); + } sessionInitialize(); doProcessRequest(); diff --git a/src/webui/webapplication.h b/src/webui/webapplication.h index e96b25300..f1a38bd3f 100644 --- a/src/webui/webapplication.h +++ b/src/webui/webapplication.h @@ -145,4 +145,5 @@ private: // security related bool m_isClickjackingProtectionEnabled; + bool m_isCSRFProtectionEnabled; }; diff --git a/src/webui/www/private/preferences_content.html b/src/webui/www/private/preferences_content.html index d828275cb..7cf03a480 100644 --- a/src/webui/www/private/preferences_content.html +++ b/src/webui/www/private/preferences_content.html @@ -463,6 +463,10 @@ +
+ + +
@@ -1029,6 +1033,7 @@ // Security $('clickjacking_protection_checkbox').setProperty('checked', pref.web_ui_clickjacking_protection_enabled); + $('csrf_protection_checkbox').setProperty('checked', pref.web_ui_csrf_protection_enabled); // Update my dynamic domain name $('use_dyndns_checkbox').setProperty('checked', pref.dyndns_enabled); @@ -1322,6 +1327,7 @@ settings.set('bypass_auth_subnet_whitelist', $('bypass_auth_subnet_whitelist_textarea').getProperty('value')); settings.set('web_ui_clickjacking_protection_enabled', $('clickjacking_protection_checkbox').getProperty('checked')); + settings.set('web_ui_csrf_protection_enabled', $('csrf_protection_checkbox').getProperty('checked')); // Update my dynamic domain name settings.set('dyndns_enabled', $('use_dyndns_checkbox').getProperty('checked'));