On MacOSX, QNetworkAccessManager gets into an infi

2019-07-23 05:32发布

问题:

In my cross-platform app, I use QNetworkAccessManager to send HTTP requests to my HTTP service that requires authentication. I recently upgraded to QT5, and to my complete surprise on MacOSX my app would send a massive amount of requests to the my service as fast as possible in some scenarios.

After doing some debugging, it turns out that this would only happen when I specify bad auth credentials in my requests. QNetworkAccessManager would indefinitely resend requests to my service if invalid username/password were specified in my HTTP requests.

My code has worked for a long time in previous QT versions, so I decided it has to be something with QT5.

回答1:

I stumbled upon a following enhancement that was added in QT5: https://bugreports.qt.io/browse/QTBUG-22033

Basically, the idea behind this enhancement os to check keychain for username/password if it intermediate proxy is requiring auth credentials. It turns out this was badly implemented, and this code has been added to the QNetworkAccessManager::authenticationRequired() signal, instead of being added to proxyAuthenticationRequired() signal.

The interesting part about this problem is that I don't set proxy for my application nor QNetworkAccessManager that I use. Which makes this problem so hard to debug!

Because of the bad placement, this "keychain querying" is happening with any authenticationRequired signal. The underlying getProxyAuth() method is calling "SecKeychainFindInternetPassword" with blank hostname which is matching a first "Internet Password" from my keychain and using it to send a request to my service with this new credentials. Imagine my surprise when I saw one of my other/personal passwords being sent to my HTTP service!

Not only this is a security issues, but it cause an infinite loop in your app. I opened a bug with QT about this: https://bugreports.qt.io/browse/QTBUG-30434

Is there a temporary solution? There is! I looked for a workaround to this issue for while. It is a nasty hack. But it works until QT guys get their ducks in a row. This hack works because it ensures that "SecKeychainFindInternetPassword" does not match any entries in the keychain, and therefore skipping that "keychain query".

Basically I am setting proxy hostname to " " instead of "" which will prevent any matching that causes an infite loop in my app.

Workaround:

 QNetworkProxy proxy = manager_->proxy();
 proxy.setHostName(" ");
 manager_->setProxy(proxy);

I hope this is resolved in the next version of QT, so I can remove this horrible hack.