From 087856d3d8d6d77881c3d8378a95d80d693af2dd Mon Sep 17 00:00:00 2001 From: Chocobo1 Date: Fri, 12 May 2017 14:46:32 +0800 Subject: [PATCH] [WebUI]: Implement CSRF defense Bump API version --- src/base/http/types.h | 4 +++ src/webui/abstractwebapplication.cpp | 39 ++++++++++++++++++++++++++++ src/webui/abstractwebapplication.h | 1 + src/webui/webapplication.cpp | 2 +- 4 files changed, 45 insertions(+), 1 deletion(-) diff --git a/src/base/http/types.h b/src/base/http/types.h index 1fb52fd32..bf1d53ad2 100644 --- a/src/base/http/types.h +++ b/src/base/http/types.h @@ -43,8 +43,12 @@ namespace Http const char HEADER_CONTENT_SECURITY_POLICY[] = "Content-Security-Policy"; const char HEADER_CONTENT_TYPE[] = "Content-Type"; const char HEADER_DATE[] = "Date"; + const char HEADER_HOST[] = "host"; + const char HEADER_ORIGIN[] = "origin"; + const char HEADER_REFERER[] = "referer"; const char HEADER_SET_COOKIE[] = "Set-Cookie"; const char HEADER_X_CONTENT_TYPE_OPTIONS[] = "X-Content-Type-Options"; + const char HEADER_X_FORWARDED_HOST[] = "x-forwarded-host"; const char HEADER_X_FRAME_OPTIONS[] = "X-Frame-Options"; const char HEADER_X_XSS_PROTECTION[] = "X-XSS-Protection"; diff --git a/src/webui/abstractwebapplication.cpp b/src/webui/abstractwebapplication.cpp index 77bb3b605..6a999118a 100644 --- a/src/webui/abstractwebapplication.cpp +++ b/src/webui/abstractwebapplication.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include "base/preferences.h" #include "base/utils/fs.h" @@ -113,6 +114,12 @@ Http::Response AbstractWebApplication::processRequest(const Http::Request &reque header(Http::HEADER_X_CONTENT_TYPE_OPTIONS, "nosniff"); header(Http::HEADER_CONTENT_SECURITY_POLICY, "default-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; script-src 'self' 'unsafe-inline'; object-src 'none';"); + // block cross-site requests + if (isCrossSiteRequest(request_)) { + status(401, "Unauthorized"); + return response(); + } + sessionInitialize(); if (!sessionActive() && !isAuthNeeded()) sessionStart(); @@ -368,6 +375,38 @@ QString AbstractWebApplication::saveTmpFile(const QByteArray &data) return QString(); } +bool AbstractWebApplication::isCrossSiteRequest(const Http::Request &request) const +{ + // https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet#Verifying_Same_Origin_with_Standard_Headers + + const auto isSameOrigin = [](const QUrl &left, const QUrl &right) -> bool + { + // [rfc6454] 5. Comparing Origins + return ((left.port() == right.port()) + // && (left.scheme() == right.scheme()) // not present in this context + && (left.host() == right.host())); + }; + + const QString targetOrigin = request.headers.value(Http::HEADER_X_FORWARDED_HOST, request.headers[Http::HEADER_HOST]); + const QString originValue = request.headers.value(Http::HEADER_ORIGIN); + const QString refererValue = request.headers.value(Http::HEADER_REFERER); + + if (originValue.isEmpty() && refererValue.isEmpty()) { + if ((request.path == QLatin1String("/")) || (request.path == QLatin1String("/favicon.ico"))) + return false; // normal request + return true; + } + + // sent with CORS requests, as well as with POST requests + if (!originValue.isEmpty()) + return !isSameOrigin(QUrl::fromUserInput(targetOrigin), originValue); + + if (!refererValue.isEmpty()) + return !isSameOrigin(QUrl::fromUserInput(targetOrigin), refererValue); + + return true; +} + const QStringMap AbstractWebApplication::CONTENT_TYPE_BY_EXT = { { "htm", Http::CONTENT_TYPE_HTML }, { "html", Http::CONTENT_TYPE_HTML }, diff --git a/src/webui/abstractwebapplication.h b/src/webui/abstractwebapplication.h index 0e5577eed..da4b42780 100644 --- a/src/webui/abstractwebapplication.h +++ b/src/webui/abstractwebapplication.h @@ -101,6 +101,7 @@ private: bool sessionInitialize(); QStringMap parseCookie(const Http::Request &request) const; + bool isCrossSiteRequest(const Http::Request &request) const; static void translateDocument(QString &data); diff --git a/src/webui/webapplication.cpp b/src/webui/webapplication.cpp index 0f8352c5e..e6660ad19 100644 --- a/src/webui/webapplication.cpp +++ b/src/webui/webapplication.cpp @@ -50,7 +50,7 @@ #include "webapplication.h" static const int API_VERSION = 14; -static const int API_VERSION_MIN = 13; +static const int API_VERSION_MIN = 14; const QString WWW_FOLDER = ":/www/public/"; const QString PRIVATE_FOLDER = ":/www/private/";