From 691d5e5d897c2d7ed0d3ca60ecdf9ac6ff191ae1 Mon Sep 17 00:00:00 2001 From: FranciscoPombal Date: Mon, 23 Dec 2019 17:58:08 +0000 Subject: [PATCH] WebUI: Implement "Secure" flag for session cookie Closes #11724. Option is enabled by default for users using qBittorrent's built-in HTTPS capabilities. This flag will never be set if qBittorrent is using plain HTTP. Users using HTTPS reverse proxies, like "qbt <-> (http) <-> proxy <-> (https) <-> user" should override the flag in the proxy in order to set it, if they wish to do so. --- src/base/preferences.cpp | 10 ++++++++++ src/base/preferences.h | 2 ++ src/gui/optionsdialog.cpp | 5 +++++ src/gui/optionsdialog.ui | 7 +++++++ src/webui/api/appcontroller.cpp | 3 +++ src/webui/webapplication.cpp | 2 ++ src/webui/webapplication.h | 1 + src/webui/www/private/views/preferences.html | 7 +++++++ 8 files changed, 37 insertions(+) diff --git a/src/base/preferences.cpp b/src/base/preferences.cpp index 2bbb5e33f..41222b571 100644 --- a/src/base/preferences.cpp +++ b/src/base/preferences.cpp @@ -653,6 +653,16 @@ void Preferences::setWebUiCSRFProtectionEnabled(const bool enabled) setValue("Preferences/WebUI/CSRFProtection", enabled); } +bool Preferences::isWebUiSecureCookieEnabled() const +{ + return value("Preferences/WebUI/SecureCookie", true).toBool(); +} + +void Preferences::setWebUiSecureCookieEnabled(const bool enabled) +{ + setValue("Preferences/WebUI/SecureCookie", enabled); +} + bool Preferences::isWebUIHostHeaderValidationEnabled() const { return value("Preferences/WebUI/HostHeaderValidation", true).toBool(); diff --git a/src/base/preferences.h b/src/base/preferences.h index d141e1639..5cfd98020 100644 --- a/src/base/preferences.h +++ b/src/base/preferences.h @@ -202,6 +202,8 @@ public: void setWebUiClickjackingProtectionEnabled(bool enabled); bool isWebUiCSRFProtectionEnabled() const; void setWebUiCSRFProtectionEnabled(bool enabled); + bool isWebUiSecureCookieEnabled () const; + void setWebUiSecureCookieEnabled(bool enabled); bool isWebUIHostHeaderValidationEnabled() const; void setWebUIHostHeaderValidationEnabled(bool enabled); diff --git a/src/gui/optionsdialog.cpp b/src/gui/optionsdialog.cpp index 95cb6c604..57fe7a9c5 100644 --- a/src/gui/optionsdialog.cpp +++ b/src/gui/optionsdialog.cpp @@ -409,6 +409,8 @@ OptionsDialog::OptionsDialog(QWidget *parent) connect(m_ui->spinSessionTimeout, qSpinBoxValueChanged, this, &ThisType::enableApplyButton); connect(m_ui->checkClickjacking, &QCheckBox::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkCSRFProtection, &QCheckBox::toggled, this, &ThisType::enableApplyButton); + connect(m_ui->checkWebUiHttps, &QGroupBox::toggled, m_ui->checkSecureCookie, &QWidget::setEnabled); + connect(m_ui->checkSecureCookie, &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); @@ -793,6 +795,7 @@ void OptionsDialog::saveOptions() // Security pref->setWebUiClickjackingProtectionEnabled(m_ui->checkClickjacking->isChecked()); pref->setWebUiCSRFProtectionEnabled(m_ui->checkCSRFProtection->isChecked()); + pref->setWebUiSecureCookieEnabled(m_ui->checkSecureCookie->isChecked()); pref->setWebUIHostHeaderValidationEnabled(m_ui->groupHostHeaderValidation->isChecked()); // DynDNS pref->setDynDNSEnabled(m_ui->checkDynDNS->isChecked()); @@ -1165,6 +1168,8 @@ void OptionsDialog::loadOptions() // Security m_ui->checkClickjacking->setChecked(pref->isWebUiClickjackingProtectionEnabled()); m_ui->checkCSRFProtection->setChecked(pref->isWebUiCSRFProtectionEnabled()); + m_ui->checkSecureCookie->setEnabled(pref->isWebUiHttpsEnabled()); + m_ui->checkSecureCookie->setChecked(pref->isWebUiSecureCookieEnabled()); m_ui->groupHostHeaderValidation->setChecked(pref->isWebUIHostHeaderValidationEnabled()); m_ui->checkDynDNS->setChecked(pref->isDynDNSEnabled()); diff --git a/src/gui/optionsdialog.ui b/src/gui/optionsdialog.ui index 3ede266bf..7e715fa60 100644 --- a/src/gui/optionsdialog.ui +++ b/src/gui/optionsdialog.ui @@ -3054,6 +3054,13 @@ Specify an IPv4 or IPv6 address. You can specify "0.0.0.0" for any IPv + + + + Enable cookie Secure flag (requires HTTPS) + + + diff --git a/src/webui/api/appcontroller.cpp b/src/webui/api/appcontroller.cpp index 04d3ae4a5..a8352a17b 100644 --- a/src/webui/api/appcontroller.cpp +++ b/src/webui/api/appcontroller.cpp @@ -239,6 +239,7 @@ void AppController::preferencesAction() // Security data["web_ui_clickjacking_protection_enabled"] = pref->isWebUiClickjackingProtectionEnabled(); data["web_ui_csrf_protection_enabled"] = pref->isWebUiCSRFProtectionEnabled(); + data["web_ui_secure_cookie_enabled"] = pref->isWebUiSecureCookieEnabled(); data["web_ui_host_header_validation_enabled"] = pref->isWebUIHostHeaderValidationEnabled(); // Update my dynamic domain name data["dyndns_enabled"] = pref->isDynDNSEnabled(); @@ -608,6 +609,8 @@ void AppController::setPreferencesAction() pref->setWebUiClickjackingProtectionEnabled(it.value().toBool()); if (hasKey("web_ui_csrf_protection_enabled")) pref->setWebUiCSRFProtectionEnabled(it.value().toBool()); + if (hasKey("web_ui_secure_cookie_enabled")) + pref->setWebUiSecureCookieEnabled(it.value().toBool()); if (hasKey("web_ui_host_header_validation_enabled")) pref->setWebUIHostHeaderValidationEnabled(it.value().toBool()); // Update my dynamic domain name diff --git a/src/webui/webapplication.cpp b/src/webui/webapplication.cpp index a1ea0c51a..41a3b4e59 100644 --- a/src/webui/webapplication.cpp +++ b/src/webui/webapplication.cpp @@ -337,6 +337,7 @@ void WebApplication::configure() m_isClickjackingProtectionEnabled = pref->isWebUiClickjackingProtectionEnabled(); m_isCSRFProtectionEnabled = pref->isWebUiCSRFProtectionEnabled(); + m_isSecureCookieEnabled = pref->isWebUiSecureCookieEnabled(); m_isHostHeaderValidationEnabled = pref->isWebUIHostHeaderValidationEnabled(); m_isHttpsEnabled = pref->isWebUiHttpsEnabled(); @@ -535,6 +536,7 @@ void WebApplication::sessionStart() QNetworkCookie cookie(C_SID, m_currentSession->id().toUtf8()); cookie.setHttpOnly(true); + cookie.setSecure(m_isSecureCookieEnabled && m_isHttpsEnabled); cookie.setPath(QLatin1String("/")); QByteArray cookieRawForm = cookie.toRawForm(); if (m_isCSRFProtectionEnabled) diff --git a/src/webui/webapplication.h b/src/webui/webapplication.h index fdb62f714..cc716cb43 100644 --- a/src/webui/webapplication.h +++ b/src/webui/webapplication.h @@ -153,6 +153,7 @@ private: QStringList m_domainList; bool m_isClickjackingProtectionEnabled; bool m_isCSRFProtectionEnabled; + bool m_isSecureCookieEnabled; bool m_isHostHeaderValidationEnabled; bool m_isHttpsEnabled; QString m_contentSecurityPolicy; diff --git a/src/webui/www/private/views/preferences.html b/src/webui/www/private/views/preferences.html index b021b19a9..193376fa8 100644 --- a/src/webui/www/private/views/preferences.html +++ b/src/webui/www/private/views/preferences.html @@ -756,6 +756,10 @@ +
+ + +
@@ -1350,6 +1354,7 @@ const isUseHttpsEnabled = $('use_https_checkbox').getProperty('checked'); $('ssl_cert_text').setProperty('disabled', !isUseHttpsEnabled); $('ssl_key_text').setProperty('disabled', !isUseHttpsEnabled); + $('secureCookieCheckbox').setProperty('disabled', !isUseHttpsEnabled); }; const updateBypasssAuthSettings = function() { @@ -1717,6 +1722,7 @@ // Security $('clickjacking_protection_checkbox').setProperty('checked', pref.web_ui_clickjacking_protection_enabled); $('csrf_protection_checkbox').setProperty('checked', pref.web_ui_csrf_protection_enabled); + $('secureCookieCheckbox').setProperty('checked', pref.web_ui_secure_cookie_enabled); $('host_header_validation_checkbox').setProperty('checked', pref.web_ui_host_header_validation_enabled); updateHostHeaderValidationSettings(); @@ -2082,6 +2088,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_secure_cookie_enabled', $('secureCookieCheckbox').getProperty('checked')); settings.set('web_ui_host_header_validation_enabled', $('host_header_validation_checkbox').getProperty('checked')); // Update my dynamic domain name