diff options
author | Kurt Pattyn <pattyn.kurt@gmail.com> | 2014-02-10 21:33:25 +0100 |
---|---|---|
committer | The Qt Project <gerrit-noreply@qt-project.org> | 2014-02-11 12:46:40 +0100 |
commit | de92bb09b12ff95bc9d03f930f54463a336f6263 (patch) | |
tree | 684e2f563be156d54fd3acbefd8bd37f68067e4f /src | |
parent | 4c4cbf55f0a2e3d634b558079e48774937dd5773 (diff) |
Check on newline characters in origin and urls
New line characters (\r\n) in the resource part of a url and in the origin
string can be used to forge the http header and can lead to insertion of
unwanted header entries. This can be an indication of an attack,
so QWebSocket immediately refuses a connection.
Change-Id: I9cdb309bfbe7025ad675925e6ea3e038476a1fd6
Reviewed-by: Frederik Gladhorn <frederik.gladhorn@digia.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/websockets/qwebsocket.cpp | 5 | ||||
-rw-r--r-- | src/websockets/qwebsocket_p.cpp | 46 | ||||
-rw-r--r-- | src/websockets/qwebsockethandshakeresponse.cpp | 32 |
3 files changed, 68 insertions, 15 deletions
diff --git a/src/websockets/qwebsocket.cpp b/src/websockets/qwebsocket.cpp index f2a2b6a..0a0c420 100644 --- a/src/websockets/qwebsocket.cpp +++ b/src/websockets/qwebsocket.cpp @@ -255,6 +255,8 @@ QT_BEGIN_NAMESPACE * The \a origin of the client is as specified \l {http://tools.ietf.org/html/rfc6454}{RFC 6454}. * (The \a origin is not required for non-web browser clients * (see \l {http://tools.ietf.org/html/rfc6455}{RFC 6455})). + * The \a origin may not contain new line characters, otherwise the connection will be + * aborted immediately during the handshake phase. * \note Currently only V13 (\l {http://tools.ietf.org/html/rfc6455} {RFC 6455}) is supported */ QWebSocket::QWebSocket(const QString &origin, @@ -373,6 +375,9 @@ void QWebSocket::close(QWebSocketProtocol::CloseCode closeCode, const QString &r /*! \brief Opens a websocket connection using the given \a url. + + If the url contains newline characters (\\r\\n), then the error signal will be emitted + with QAbstractSocket::ConnectionRefusedError as error type. */ void QWebSocket::open(const QUrl &url) { diff --git a/src/websockets/qwebsocket_p.cpp b/src/websockets/qwebsocket_p.cpp index 1c4baca..d54337f 100644 --- a/src/websockets/qwebsocket_p.cpp +++ b/src/websockets/qwebsocket_p.cpp @@ -330,8 +330,14 @@ void QWebSocketPrivate::close(QWebSocketProtocol::CloseCode closeCode, QString r void QWebSocketPrivate::open(const QUrl &url, bool mask) { //just delete the old socket for the moment; - //later, we can add more 'intelligent' handling by looking at the url + //later, we can add more 'intelligent' handling by looking at the URL //m_pSocket.reset(); + Q_Q(QWebSocket); + if (!url.isValid() || url.toString().contains(QStringLiteral("\r\n"))) { + setErrorString(QWebSocket::tr("Invalid URL.")); + Q_EMIT q->error(QAbstractSocket::ConnectionRefusedError); + return; + } QTcpSocket *pTcpSocket = m_pSocket.take(); if (pTcpSocket) { releaseConnections(pTcpSocket); @@ -339,14 +345,18 @@ void QWebSocketPrivate::open(const QUrl &url, bool mask) } //if (m_url != url) if (Q_LIKELY(!m_pSocket)) { - Q_Q(QWebSocket); - m_dataProcessor.clear(); m_isClosingHandshakeReceived = false; m_isClosingHandshakeSent = false; setRequestUrl(url); QString resourceName = url.path(); + if (resourceName.contains(QStringLiteral("\r\n"))) { + setRequestUrl(QUrl()); //clear requestUrl + setErrorString(QWebSocket::tr("Invalid resource name.")); + Q_EMIT q->error(QAbstractSocket::ConnectionRefusedError); + return; + } if (!url.query().isEmpty()) { if (!resourceName.endsWith(QChar::fromLatin1('?'))) { resourceName.append(QChar::fromLatin1('?')); @@ -973,6 +983,11 @@ void QWebSocketPrivate::processStateChanged(QAbstractSocket::SocketState socketS QString(), QString(), m_key); + if (handshake.isEmpty()) { + m_pSocket->abort(); + Q_EMIT q->error(QAbstractSocket::ConnectionRefusedError); + return; + } m_pSocket->write(handshake.toLatin1()); } break; @@ -1062,6 +1077,31 @@ QString QWebSocketPrivate::createHandShakeRequest(QString resourceName, QByteArray key) { QStringList handshakeRequest; + if (resourceName.contains(QStringLiteral("\r\n"))) { + setErrorString(QWebSocket::tr("The resource name contains newlines. " \ + "Possible attack detected.")); + return QString(); + } + if (host.contains(QStringLiteral("\r\n"))) { + setErrorString(QWebSocket::tr("The hostname contains newlines. " \ + "Possible attack detected.")); + return QString(); + } + if (origin.contains(QStringLiteral("\r\n"))) { + setErrorString(QWebSocket::tr("The origin contains newlines. " \ + "Possible attack detected.")); + return QString(); + } + if (extensions.contains(QStringLiteral("\r\n"))) { + setErrorString(QWebSocket::tr("The extensions attribute contains newlines. " \ + "Possible attack detected.")); + return QString(); + } + if (protocols.contains(QStringLiteral("\r\n"))) { + setErrorString(QWebSocket::tr("The protocols attribute contains newlines. " \ + "Possible attack detected.")); + return QString(); + } handshakeRequest << QStringLiteral("GET ") % resourceName % QStringLiteral(" HTTP/1.1") << QStringLiteral("Host: ") % host << diff --git a/src/websockets/qwebsockethandshakeresponse.cpp b/src/websockets/qwebsockethandshakeresponse.cpp index 37c0636..fd2ccd5 100644 --- a/src/websockets/qwebsockethandshakeresponse.cpp +++ b/src/websockets/qwebsockethandshakeresponse.cpp @@ -177,19 +177,27 @@ QString QWebSocketHandshakeResponse::getHandshakeResponse( response << QStringLiteral("Sec-WebSocket-Extensions: ") % m_acceptedExtension; } QString origin = request.origin().trimmed(); - if (origin.isEmpty()) - origin = QStringLiteral("*"); - response << QStringLiteral("Server: ") % serverName << - QStringLiteral("Access-Control-Allow-Credentials: false") << - QStringLiteral("Access-Control-Allow-Methods: GET") << - QStringLiteral("Access-Control-Allow-Headers: content-type") << - QStringLiteral("Access-Control-Allow-Origin: ") % origin << - QStringLiteral("Date: ") % - QDateTime::currentDateTimeUtc() - .toString(QStringLiteral("ddd, dd MMM yyyy hh:mm:ss 'GMT'")); + if (origin.contains(QStringLiteral("\r\n")) || + serverName.contains(QStringLiteral("\r\n"))) { + m_error = QWebSocketProtocol::CloseCodeAbnormalDisconnection; + m_errorString = tr("One of the headers contains a newline. " \ + "Possible attack detected."); + m_canUpgrade = false; + } else { + if (origin.isEmpty()) + origin = QStringLiteral("*"); + response << QStringLiteral("Server: ") % serverName << + QStringLiteral("Access-Control-Allow-Credentials: false") << + QStringLiteral("Access-Control-Allow-Methods: GET") << + QStringLiteral("Access-Control-Allow-Headers: content-type") << + QStringLiteral("Access-Control-Allow-Origin: ") % origin << + QStringLiteral("Date: ") % + QDateTime::currentDateTimeUtc() + .toString(QStringLiteral("ddd, dd MMM yyyy hh:mm:ss 'GMT'")); - m_acceptedVersion = QWebSocketProtocol::currentVersion(); - m_canUpgrade = true; + m_acceptedVersion = QWebSocketProtocol::currentVersion(); + m_canUpgrade = true; + } } } else { m_error = QWebSocketProtocol::CloseCodeProtocolError; |