Revise cookie 'secure flag' enable condition

The localhost is 'potentially trustworthy' and RFC 6265 allows setting secure flag in this case.
Also check `X-Forwarded-Proto` header value to support reverse proxy usage.

Note: for reverse proxy users, now the `X-Forwarded-Proto` header is expected to be sent to qbt
otherwise the `secure` flag might be set erroneously.

https://datatracker.ietf.org/doc/html/rfc6265#section-4.1.2.5
https://w3c.github.io/webappsec-secure-contexts/#is-origin-trustworthy

Closes #21250.
PR #21260.
This commit is contained in:
Chocobo1
2024-09-07 21:38:27 +08:00
committed by GitHub
parent d9bc7935eb
commit 130c0d8487
6 changed files with 27 additions and 6 deletions

View File

@@ -744,7 +744,7 @@ void WebApplication::sessionStart()
QNetworkCookie cookie {m_sessionCookieName.toLatin1(), m_currentSession->id().toLatin1()};
cookie.setHttpOnly(true);
cookie.setSecure(m_isSecureCookieEnabled && m_isHttpsEnabled);
cookie.setSecure(m_isSecureCookieEnabled && isOriginTrustworthy()); // [rfc6265] 4.1.2.5. The Secure Attribute
cookie.setPath(u"/"_s);
if (m_isCSRFProtectionEnabled)
cookie.setSameSitePolicy(QNetworkCookie::SameSite::Strict);
@@ -767,6 +767,27 @@ void WebApplication::sessionEnd()
setHeader({Http::HEADER_SET_COOKIE, QString::fromLatin1(cookie.toRawForm())});
}
bool WebApplication::isOriginTrustworthy() const
{
// https://w3c.github.io/webappsec-secure-contexts/#is-origin-trustworthy
if (m_isReverseProxySupportEnabled)
{
const QString forwardedProto = request().headers.value(Http::HEADER_X_FORWARDED_PROTO);
if (forwardedProto.compare(u"https", Qt::CaseInsensitive) == 0)
return true;
}
if (m_isHttpsEnabled)
return true;
// client is on localhost
if (env().clientAddress.isLoopback())
return true;
return false;
}
bool WebApplication::isCrossSiteRequest(const Http::Request &request) const
{
// https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet#Verifying_Same_Origin_with_Standard_Headers

View File

@@ -128,9 +128,11 @@ private:
bool isAuthNeeded();
bool isPublicAPI(const QString &scope, const QString &action) const;
bool isOriginTrustworthy() const;
bool isCrossSiteRequest(const Http::Request &request) const;
bool validateHostHeader(const QStringList &domains) const;
// reverse proxy
QHostAddress resolveClientAddress() const;
// Persistent data

View File

@@ -988,7 +988,7 @@
</div>
<div class="formRow">
<input type="checkbox" id="secureCookieCheckbox">
<label for="secureCookieCheckbox">QBT_TR(Enable cookie Secure flag (requires HTTPS))QBT_TR[CONTEXT=OptionsDialog]</label>
<label for="secureCookieCheckbox">QBT_TR(Enable cookie Secure flag (requires HTTPS or localhost connection))QBT_TR[CONTEXT=OptionsDialog]</label>
</div>
<fieldset class="settings">
@@ -1965,7 +1965,6 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD
const isUseHttpsEnabled = $("use_https_checkbox").checked;
$("ssl_cert_text").disabled = !isUseHttpsEnabled;
$("ssl_key_text").disabled = !isUseHttpsEnabled;
$("secureCookieCheckbox").disabled = !isUseHttpsEnabled;
};
const updateBypasssAuthSettings = function() {