From 7c2437e5f27c76933c4e1ec3f4734f6cec6c22d8 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 c5becb6e5..598e65348 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(); @@ -372,6 +379,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; +} + QStringMap AbstractWebApplication::initializeContentTypeByExtMap() { QStringMap map; diff --git a/src/webui/abstractwebapplication.h b/src/webui/abstractwebapplication.h index e8fcd5b93..ed4492934 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 8672a85b9..efad107b4 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/";