CVE-2023-32762: Insufficient Information (NVD-CWE-noinfo)
value: An issue was discovered in Qt before 5.15.14, 6.x before 6.2.9, and 6.3.x through 6.5.x before 6.5.1. Qt Network incorrectly parses the strict-transport-security (HSTS) header, allowing unencrypted connections to be established, even when explicitly prohibited by the server. This happens if the case used for this header does not exactly match.
CWE description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
CVSS v3 base score: 5.3
Published: 2023-05-28T23:15Z
Repository: https://github.com/qt/qtbase
Language: C++
Files changed: src/network/access/qhsts.cpp, tests/auto/network/access/hsts/tst_qhsts.cpp
Commit date: 2023-05-08 20:28:46 +0200
Commit message: Hsts: match header names case insensitively

Header field names are always considered to be case-insensitive.

Pick-to: 6.5 6.5.1 6.2 5.15
Fixes: QTBUG-113392
Change-Id: Ifb4def4bb7f2ac070416cdc76581a769f1e52b43
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>

Diff: diff --git a/src/network/access/qhsts.cpp b/src/network/access/qhsts.cpp
index 39905f35480..82deede1729 100644
--- a/src/network/access/qhsts.cpp
+++ b/src/network/access/qhsts.cpp
@@ -325,12 +325,12 @@ quoted-pair    = "\" CHAR
 */
 
 bool QHstsHeaderParser::parse(const QList<QPair<QByteArray, QByteArray>> &headers)
 {
     for (const auto &h : headers) {
-        // We use '==' since header name was already 'trimmed' for us:
-        if (h.first == "Strict-Transport-Security") {
+        // We compare directly because header name was already 'trimmed' for us:
+        if (h.first.compare("Strict-Transport-Security", Qt::CaseInsensitive) == 0) {
             header = h.second;
             // RFC6797, 8.1:
             //
             //  The UA MUST ignore any STS header fields not conforming to the
             // grammar specified in Section 6.1 ("Strict-Transport-Security HTTP
diff --git a/tests/auto/network/access/hsts/tst_qhsts.cpp b/tests/auto/network/access/hsts/tst_qhsts.cpp
index 252f5e8f579..97a2d2889e5 100644
--- a/tests/auto/network/access/hsts/tst_qhsts.cpp
+++ b/tests/auto/network/access/hsts/tst_qhsts.cpp
@@ -214,10 +214,16 @@ void tst_QHsts::testSTSHeaderParser()
     list << Header("Strict-Transport-Security", "includeSubDomains;max-age=1000");
     QVERIFY(parser.parse(list));
     QVERIFY(parser.expirationDate() > QDateTime::currentDateTimeUtc());
     QVERIFY(parser.includeSubDomains());
 
+    list.pop_back();
+    list << Header("strict-transport-security", "includeSubDomains;max-age=1000");
+    QVERIFY(parser.parse(list));
+    QVERIFY(parser.expirationDate() > QDateTime::currentDateTimeUtc());
+    QVERIFY(parser.includeSubDomains());
+
     list.pop_back();
     // Invalid (includeSubDomains twice):
     list << Header("Strict-Transport-Security", "max-age = 1000 ; includeSubDomains;includeSubDomains");
     QVERIFY(!parser.parse(list));
     QVERIFY(!parser.includeSubDomains());

Vulnerable code:         // We use '==' since header name was already 'trimmed' for us:
        if (h.first == "Strict-Transport-Security") {
Fixed code:         // We compare directly because header name was already 'trimmed' for us:
        if (h.first.compare("Strict-Transport-Security", Qt::CaseInsensitive) == 0) {
    list.pop_back();
    list << Header("strict-transport-security", "includeSubDomains;max-age=1000");
    QVERIFY(parser.parse(list));
    QVERIFY(parser.expirationDate() > QDateTime::currentDateTimeUtc());
    QVERIFY(parser.includeSubDomains());