QTcpServer & DoS Attacks
-
Hello together,
i have a small problem and i really have no idea how to manage that. I have a async single threaded REST Http server based on QTcpServer with QSslSockets. Everything is based on Signal & Slot, so nothing like waitFor... is implemented. In the same Thread i have a REST resources which does stuff. For example changing a value every 500ms. Data size of each REST call is pretty low. Some JSON objects. Maybe 1kB up to 50 kB, round about that.
So my problem is now, if there is a funny user who flood me with message (Just hold enter in the REST Browser) i can't hold the 500ms - i see that on the debug output. Is there any way to prevent on such an issue?
Hope u guys can help me.
-
Hi,
First thing you can do is move the "resources" work in its own thread. Then you can also implement throttling so if a user does too many queries you stop responding to him
Hope it helps
-
What I've done until now is, that i put the SSL socket connection in a seperate thread and wait with the blocking methods until a valid connection is established. After that i give the established socket back to the main thread. So my main thread doesn't have to handle unestablished socket connects. This is in my opinion a hack but it give a bit of optimization.
I although tried to use instead of the socket thread option the pendingConnection option of the QTcpSocket. See code below. The problem on this approach is, that in future i always get: "Start server encryption" - "Remove unestablished connection". And this in a loop. So i do not get a valid connection anymore. "Netstate" tells me, that there are a lot of CLOSE_WAIT socket connections. But i pretty sure i closed everything. If i do not call handleConnection(...) in sslSocketDisconnected the pendingConnection blocks any incoming connection. The QTcpSocket does not remove Unconnected Sockets out of its queue. So i have to call "nextPendingConnection". Independent if the connection is established or not.
To the thread approach:
I thought about that. But i can't make a decide if there is a real advantage. Because lets stay on the example above. There is a RestTimerResource with does something every 500ms. Now there are a lot of GETs on this resource. Isn't this one thread. Or do you mean to put the read/write operations (byte parsing) in one Thread and if there is a message receive, this will be handled by the main thread. So searching the right Resource, getting the information and send this information back in a seperat thread again?!One side info. When i run that on my PC i have less problems. If i run that on my embedded system or with valgrind on the PC then i have that problem.
I'm now working a whole week on that problem with a lots of hours.... Very frustrating...
@
/****************************************************************************
- Global
*/
using namespace Core::Http::Internal;
/****************************************************************************
-
Public
*/
BHttpsServer::BHttpsServer(QObject *parent) :
BHttpServer(parent)
{
#if (BTOOL_ARCHITECTURE == arm || BTOOL_ARCHITECTURE == linux-arm)
QFile sslCertificate(":/certs/certificate.crt");
QFile sslPrivateKey(":/certs/private.key");
#else
QFile sslCertificate("/opt/b/certs/certificate.crt");
QFile sslPrivateKey("/opt/b/certs/private.key");
#endifif(!sslCertificate.open(QIODevice::ReadOnly)) {
bCritical() << "No certificate found";
Q_ASSERT(false);
return;
}if(!sslPrivateKey.open(QIODevice::ReadOnly)) {
bCritical() << "No private key found";
Q_ASSERT(false);
return;
}QSslCertificate cert(&sslCertificate);
QSslKey key(&sslPrivateKey, QSsl::Rsa);m_sslConfiguration = QSslConfiguration::defaultConfiguration();
m_sslConfiguration.setLocalCertificate(cert);
m_sslConfiguration.setPrivateKey(key);setMaxPendingConnections(1);
}
BHttpsServer::~BHttpsServer()
{}void BHttpsServer::incomingConnection(qintptr socketDescriptor)
{
QSslSocket *sslSocket = new QSslSocket;
if (sslSocket->setSocketDescriptor(socketDescriptor)) {
QString peer = Core::Http::peerToString(*sslSocket);
bDebug() << "Start SSL server encryption with " << peer;connect(sslSocket, SIGNAL(encrypted()), this, SLOT(sslConnectionReady())); connect(sslSocket, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(sslErrors(QList<QSslError>))); connect(sslSocket, SIGNAL(disconnected()), this, SLOT(sslSocketDisconnected())); sslSocket->setSslConfiguration(m_sslConfiguration); sslSocket->setPeerVerifyMode(QSslSocket::VerifyNone); addPendingConnection(sslSocket); sslSocket->startServerEncryption(); } else { bWarning() << "Unable to create SSL socket connection"; delete sslSocket; }
}
void BHttpsServer::sslConnectionReady()
{
QSslSocket sslSocket = static_cast<QSslSocket>(QObject::sender());
disconnect(sslSocket, SIGNAL(disconnected()),
this, SLOT(sslSocketDisconnected()));
QString peer = Core::Http::peerToString(*sslSocket);
bDebug() << "Server encryption established with " << peer;handleSslConnection();
}
void BHttpsServer::sslErrors(QList<QSslError> errors)
{
bWarning() << "SSL socket errors detected: ";
foreach(QSslError err, errors) {
QSslError::SslError errorCode = err.error();
QString sslErrorString = err.errorString();
bWarning() << "SSL error #" << errorCode << ": "
<< sslErrorString;
}
}void BHttpsServer::sslSocketDisconnected()
{
QSslSocket sslSocket = static_cast<QSslSocket>(QObject::sender());
QString peer = Core::Http::peerToString(*sslSocket);
bDebug() << "Remove unestablished SSL socket from" << peer;//Workaround: QTcpServer does not remove unestabished connections by it self. m_unestablishedSockets.append(sslSocket); handleSslConnection();
}
void BHttpsServer::handleSslConnection()
{
while(hasPendingConnections()) {
QSslSocket client = static_cast<QSslSocket>(nextPendingConnection());
if(m_unestablishedSockets.contains(client)) {
m_unestablishedSockets.removeAll(client);
client->close();
} else {
BHttpMessageInfo *msgInfo = new BHttpMessageInfo(qMakePair(client, true));
if(!m_httpHandler->receive(msgInfo, true)) {
QString peer = Core::Http::peerToString(*client);
bWarning() << "Failed to read data from " << peer;
}
}
}
}
@ - Global