Secure and efficient QNetworkAccessManager use
Doing HTTP operations with Qt is relatively straightforward, but there are also a few pitfalls, unexpected default settings and low-hanging performance improvements around it worth keeping in mind.
The most common and most easily to make mistake when working with
QNetworkAccessManager is probably
missing to delete finished replies, and thus leaking reply objects over time. Make sure to call
QNetworkReply instances in response to their
Use transport security
That is, use URLs starting with
https: rather than
http:. That might sound obvious, but the little
easy to miss.
So, pay extra attention to hardcoded URLs and think about how to deal with URLs taken from user input. In the best
https: scheme can just be enforced unconditionally, but that might not be viable everywhere.
Minimize QNetworkAccessManager instances
You don’t need a
QNetworkAccessManager per request, in theory one is enough. In practice you might end up with one per thread
(e.g. in case of QML), but more than that should have a good justification.
There’s two reasons for this:
QNetworkAccessManagercontains logic for request queuing and network connection reuse, which is bypassed by using multiple instances, so you are missing out on useful optimizations.
- More instances increase the risk of missing important setup and configuration on one of them (see below), centralizing instance creation in one location is therefore usually a good idea
This also implies that you generally want to prefer
QNetworkReply signals over
QNetworkAccessManager signals for handling
results or errors. This avoids interference when a
QNetworkAccessManager is used by other components as well. It’s also worth
checking whether components which do HTTP requests internally can use an externally provided
The QML engine is a common example (see
Looking at properly setting up a
QNetworkAccessManager instance, the most common issue is probably the redirection behavior,
something that has caused us quite some operational headaches in the past. Rather unintuitively, in Qt 5 redirection is disabled by default.
Starting with Qt 6, the redirection behavior
NoLessSafeRedirectPolicy is the default.
HTTP Strict Transport Security (HSTS)
Another thing you probably want to enable in practically all cases is HTTP Strict Transport Security (HSTS). This involves managing persistent state, so this not just needs to be enabled, but also needs a storage location.
QStandardPaths::CacheLocation is a good default, for shared components/libraries
QStandardPaths::GenericCacheLocation might be more appropriate so the HSTS state is shared among all users.
SSL error handling
Transport security errors are fatal by default, and that is usually what you want. One exception from this are self-signed server certificates, but thanks to Let’s Encrypt that has become increasingly uncommon as well.
If self-signed certificated need to be supported,
QNetworkAccessManager unfortunately makes it very easy
to just ignore all possible SSL errors, rather than just accept the unknown server certificate signature.
KIO::SslUI has methods to help with that, including asking for user-confirmation and persisting choices.
QNetworkAccessManager doesn’t do any caching, every reply comes from a full request to the server.
It’s however possible to enable the use of HTTP caching and have a persistent on disk cache for this.
Whether or not that makes sense needs to be looked at on a case-by-case basis though. Using real-time data or API (e.g. KPublicTransport) or having higher-level caching (e.g. KWeatherCore, KOSMIndoorMap) might not benefit from that, read-only assets used over a longer period of time on the other hand are ideal (e.g. avatar images in Tokodon).
The same considerations for the storage locations as for the HSTS state apply here as well.
Does this matter?
For the security-related aspects I hopefully don’t have to argue why we should care, so let’s just look at the impact of disk caches. Here are some numbers:
- Caching the conference list for Kongress cut down the transfer volume per application start by about 20%.
- Adding a disk cache to Tokodon reduced transfer volume on a “warm” start by up to 80%.
How much can be saved varies greatly depending on the specific application, but it’s clearly worth looking into.