From 344e47dcfbf242d8213e22de6d2ae58c0b6df09e Mon Sep 17 00:00:00 2001 From: Chocobo1 Date: Fri, 16 Nov 2018 13:41:27 +0800 Subject: [PATCH] Add option for WebUI Host header validation Closes #9743. --- src/base/preferences.cpp | 10 ++++ src/base/preferences.h | 2 + src/gui/optionsdialog.cpp | 3 + src/gui/optionsdialog.ui | 56 +++++++++++-------- src/webui/api/appcontroller.cpp | 3 + src/webui/webapplication.cpp | 3 +- src/webui/webapplication.h | 1 + .../www/private/preferences_content.html | 12 ++++ 8 files changed, 67 insertions(+), 23 deletions(-) diff --git a/src/base/preferences.cpp b/src/base/preferences.cpp index d10799b0d..6bebb22c1 100644 --- a/src/base/preferences.cpp +++ b/src/base/preferences.cpp @@ -626,6 +626,16 @@ void Preferences::setWebUiCSRFProtectionEnabled(bool enabled) setValue("Preferences/WebUI/CSRFProtection", enabled); } +bool Preferences::isWebUIHostHeaderValidationEnabled() const +{ + return value("Preferences/WebUI/HostHeaderValidation", true).toBool(); +} + +void Preferences::setWebUIHostHeaderValidationEnabled(const bool enabled) +{ + setValue("Preferences/WebUI/HostHeaderValidation", 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 2f1658e12..26e21f60b 100644 --- a/src/base/preferences.h +++ b/src/base/preferences.h @@ -201,6 +201,8 @@ public: void setWebUiClickjackingProtectionEnabled(bool enabled); bool isWebUiCSRFProtectionEnabled() const; void setWebUiCSRFProtectionEnabled(bool enabled); + bool isWebUIHostHeaderValidationEnabled() const; + void setWebUIHostHeaderValidationEnabled(bool enabled); // HTTPS bool isWebUiHttpsEnabled() const; diff --git a/src/gui/optionsdialog.cpp b/src/gui/optionsdialog.cpp index 1f440c176..ad186c9f4 100644 --- a/src/gui/optionsdialog.cpp +++ b/src/gui/optionsdialog.cpp @@ -390,6 +390,7 @@ OptionsDialog::OptionsDialog(QWidget *parent) 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->groupHostHeaderValidation, &QGroupBox::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); @@ -719,6 +720,7 @@ void OptionsDialog::saveOptions() // Security pref->setWebUiClickjackingProtectionEnabled(m_ui->checkClickjacking->isChecked()); pref->setWebUiCSRFProtectionEnabled(m_ui->checkCSRFProtection->isChecked()); + pref->setWebUIHostHeaderValidationEnabled(m_ui->groupHostHeaderValidation->isChecked()); // DynDNS pref->setDynDNSEnabled(m_ui->checkDynDNS->isChecked()); pref->setDynDNSService(m_ui->comboDNSService->currentIndex()); @@ -1082,6 +1084,7 @@ void OptionsDialog::loadOptions() // Security m_ui->checkClickjacking->setChecked(pref->isWebUiClickjackingProtectionEnabled()); m_ui->checkCSRFProtection->setChecked(pref->isWebUiCSRFProtectionEnabled()); + m_ui->groupHostHeaderValidation->setChecked(pref->isWebUIHostHeaderValidationEnabled()); m_ui->checkDynDNS->setChecked(pref->isDynDNSEnabled()); m_ui->comboDNSService->setCurrentIndex(static_cast(pref->getDynDNSService())); diff --git a/src/gui/optionsdialog.ui b/src/gui/optionsdialog.ui index 36eb69d11..12eacded5 100644 --- a/src/gui/optionsdialog.ui +++ b/src/gui/optionsdialog.ui @@ -2944,28 +2944,6 @@ Specify an IPv4 or IPv6 address. You can specify "0.0.0.0" for any IPv - - - - - - Server domains: - - - - - - - Whitelist for filtering HTTP Host header values. -In order to defend against DNS rebinding attack, -you should put in domain names used by WebUI server. - -Use ';' to split multiple entries. Can use wildcard '*'. - - - - - @@ -3190,6 +3168,40 @@ Use ';' to split multiple entries. Can use wildcard '*'. + + + + Enable Host header validation + + + true + + + + + + + + Server domains: + + + + + + + Whitelist for filtering HTTP Host header values. +In order to defend against DNS rebinding attack, +you should put in domain names used by WebUI server. + +Use ';' to split multiple entries. Can use wildcard '*'. + + + + + + + + diff --git a/src/webui/api/appcontroller.cpp b/src/webui/api/appcontroller.cpp index 445a3dd44..82eb7d2e1 100644 --- a/src/webui/api/appcontroller.cpp +++ b/src/webui/api/appcontroller.cpp @@ -208,6 +208,7 @@ void AppController::preferencesAction() // Security data["web_ui_clickjacking_protection_enabled"] = pref->isWebUiClickjackingProtectionEnabled(); data["web_ui_csrf_protection_enabled"] = pref->isWebUiCSRFProtectionEnabled(); + data["web_ui_host_header_validation_enabled"] = pref->isWebUIHostHeaderValidationEnabled(); // Update my dynamic domain name data["dyndns_enabled"] = pref->isDynDNSEnabled(); data["dyndns_service"] = pref->getDynDNSService(); @@ -487,6 +488,8 @@ void AppController::setPreferencesAction() 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()); + if (m.contains("web_ui_host_header_validation_enabled")) + pref->setWebUIHostHeaderValidationEnabled(m["web_ui_host_header_validation_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 602973b25..ecc57a294 100644 --- a/src/webui/webapplication.cpp +++ b/src/webui/webapplication.cpp @@ -452,6 +452,7 @@ void WebApplication::configure() m_isClickjackingProtectionEnabled = pref->isWebUiClickjackingProtectionEnabled(); m_isCSRFProtectionEnabled = pref->isWebUiCSRFProtectionEnabled(); + m_isHostHeaderValidationEnabled = pref->isWebUIHostHeaderValidationEnabled(); m_isHttpsEnabled = pref->isWebUiHttpsEnabled(); } @@ -542,7 +543,7 @@ Http::Response WebApplication::processRequest(const Http::Request &request, cons try { // block suspicious requests if ((m_isCSRFProtectionEnabled && isCrossSiteRequest(m_request)) - || !validateHostHeader(m_domainList)) { + || (m_isHostHeaderValidationEnabled && !validateHostHeader(m_domainList))) { throw UnauthorizedHTTPError(); } diff --git a/src/webui/webapplication.h b/src/webui/webapplication.h index c99db6238..cfe09bcbf 100644 --- a/src/webui/webapplication.h +++ b/src/webui/webapplication.h @@ -155,5 +155,6 @@ private: QStringList m_domainList; bool m_isClickjackingProtectionEnabled; bool m_isCSRFProtectionEnabled; + bool m_isHostHeaderValidationEnabled; bool m_isHttpsEnabled; }; diff --git a/src/webui/www/private/preferences_content.html b/src/webui/www/private/preferences_content.html index 4d20e021e..64eeef728 100644 --- a/src/webui/www/private/preferences_content.html +++ b/src/webui/www/private/preferences_content.html @@ -457,6 +457,10 @@ +
+ + +
@@ -712,6 +716,11 @@ $('bypass_auth_subnet_whitelist_textarea').setProperty('disabled', !isBypassAuthSubnetWhitelistEnabled); }; + updateHostHeaderValidationSettings = function() { + var isHostHeaderValidationEnabled = $('host_header_validation_checkbox').getProperty('checked'); + $('webui_domain_textarea').setProperty('disabled', !isHostHeaderValidationEnabled); + }; + updateDynDnsSettings = function() { var isDynDnsEnabled = $('use_dyndns_checkbox').getProperty('checked'); $('dyndns_select').setProperty('disabled', !isDynDnsEnabled); @@ -971,6 +980,8 @@ // Security $('clickjacking_protection_checkbox').setProperty('checked', pref.web_ui_clickjacking_protection_enabled); $('csrf_protection_checkbox').setProperty('checked', pref.web_ui_csrf_protection_enabled); + $('host_header_validation_checkbox').setProperty('checked', pref.web_ui_host_header_validation_enabled); + updateHostHeaderValidationSettings(); // Update my dynamic domain name $('use_dyndns_checkbox').setProperty('checked', pref.dyndns_enabled); @@ -1256,6 +1267,7 @@ settings.set('web_ui_clickjacking_protection_enabled', $('clickjacking_protection_checkbox').getProperty('checked')); settings.set('web_ui_csrf_protection_enabled', $('csrf_protection_checkbox').getProperty('checked')); + settings.set('web_ui_host_header_validation_enabled', $('host_header_validation_checkbox').getProperty('checked')); // Update my dynamic domain name settings.set('dyndns_enabled', $('use_dyndns_checkbox').getProperty('checked'));