mirror of
https://github.com/d47081/qBittorrent.git
synced 2025-02-06 03:44:29 +00:00
[WebUI]: Implement CSRF defense
Bump API version
This commit is contained in:
parent
34c7465009
commit
087856d3d8
@ -43,8 +43,12 @@ namespace Http
|
|||||||
const char HEADER_CONTENT_SECURITY_POLICY[] = "Content-Security-Policy";
|
const char HEADER_CONTENT_SECURITY_POLICY[] = "Content-Security-Policy";
|
||||||
const char HEADER_CONTENT_TYPE[] = "Content-Type";
|
const char HEADER_CONTENT_TYPE[] = "Content-Type";
|
||||||
const char HEADER_DATE[] = "Date";
|
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_SET_COOKIE[] = "Set-Cookie";
|
||||||
const char HEADER_X_CONTENT_TYPE_OPTIONS[] = "X-Content-Type-Options";
|
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_FRAME_OPTIONS[] = "X-Frame-Options";
|
||||||
const char HEADER_X_XSS_PROTECTION[] = "X-XSS-Protection";
|
const char HEADER_X_XSS_PROTECTION[] = "X-XSS-Protection";
|
||||||
|
|
||||||
|
@ -36,6 +36,7 @@
|
|||||||
#include <QNetworkCookie>
|
#include <QNetworkCookie>
|
||||||
#include <QTemporaryFile>
|
#include <QTemporaryFile>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
#include "base/preferences.h"
|
#include "base/preferences.h"
|
||||||
#include "base/utils/fs.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_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';");
|
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();
|
sessionInitialize();
|
||||||
if (!sessionActive() && !isAuthNeeded())
|
if (!sessionActive() && !isAuthNeeded())
|
||||||
sessionStart();
|
sessionStart();
|
||||||
@ -368,6 +375,38 @@ QString AbstractWebApplication::saveTmpFile(const QByteArray &data)
|
|||||||
return QString();
|
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 = {
|
const QStringMap AbstractWebApplication::CONTENT_TYPE_BY_EXT = {
|
||||||
{ "htm", Http::CONTENT_TYPE_HTML },
|
{ "htm", Http::CONTENT_TYPE_HTML },
|
||||||
{ "html", Http::CONTENT_TYPE_HTML },
|
{ "html", Http::CONTENT_TYPE_HTML },
|
||||||
|
@ -101,6 +101,7 @@ private:
|
|||||||
bool sessionInitialize();
|
bool sessionInitialize();
|
||||||
|
|
||||||
QStringMap parseCookie(const Http::Request &request) const;
|
QStringMap parseCookie(const Http::Request &request) const;
|
||||||
|
bool isCrossSiteRequest(const Http::Request &request) const;
|
||||||
|
|
||||||
static void translateDocument(QString &data);
|
static void translateDocument(QString &data);
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@
|
|||||||
#include "webapplication.h"
|
#include "webapplication.h"
|
||||||
|
|
||||||
static const int API_VERSION = 14;
|
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 WWW_FOLDER = ":/www/public/";
|
||||||
const QString PRIVATE_FOLDER = ":/www/private/";
|
const QString PRIVATE_FOLDER = ":/www/private/";
|
||||||
|
Loading…
x
Reference in New Issue
Block a user