Browse Source

[WebUI]: Implement CSRF defense

Bump API version
adaptive-webui-19844
Chocobo1 8 years ago committed by sledgehammer999
parent
commit
087856d3d8
No known key found for this signature in database
GPG Key ID: 6E4A2D025B7CC9A2
  1. 4
      src/base/http/types.h
  2. 39
      src/webui/abstractwebapplication.cpp
  3. 1
      src/webui/abstractwebapplication.h
  4. 2
      src/webui/webapplication.cpp

4
src/base/http/types.h

@ -43,8 +43,12 @@ namespace Http @@ -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";

39
src/webui/abstractwebapplication.cpp

@ -36,6 +36,7 @@ @@ -36,6 +36,7 @@
#include <QNetworkCookie>
#include <QTemporaryFile>
#include <QTimer>
#include <QUrl>
#include "base/preferences.h"
#include "base/utils/fs.h"
@ -113,6 +114,12 @@ Http::Response AbstractWebApplication::processRequest(const Http::Request &reque @@ -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) @@ -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 },

1
src/webui/abstractwebapplication.h

@ -101,6 +101,7 @@ private: @@ -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);

2
src/webui/webapplication.cpp

@ -50,7 +50,7 @@ @@ -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/";

Loading…
Cancel
Save