[WebUI]: Implement CSRF defense

Bump API version
This commit is contained in:
Chocobo1
2017-05-12 14:46:32 +08:00
committed by sledgehammer999
parent 34c7465009
commit 087856d3d8
4 changed files with 45 additions and 1 deletions

View File

@@ -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
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 },