Browse Source

Merge pull request #12793 from Chocobo1/prebuilt_headers

Prebuild HTTP headers
adaptive-webui-19844
Mike Tzou 5 years ago committed by GitHub
parent
commit
e015710ffd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      src/base/http/requestparser.cpp
  2. 4
      src/base/http/responsebuilder.cpp
  3. 2
      src/base/http/responsebuilder.h
  4. 14
      src/base/http/types.h
  5. 52
      src/webui/webapplication.cpp
  6. 11
      src/webui/webapplication.h

4
src/base/http/requestparser.cpp

@ -57,7 +57,7 @@ namespace
return in; return in;
} }
bool parseHeaderLine(const QString &line, QStringMap &out) bool parseHeaderLine(const QString &line, HeaderMap &out)
{ {
// [rfc7230] 3.2. Header Fields // [rfc7230] 3.2. Header Fields
const int i = line.indexOf(':'); const int i = line.indexOf(':');
@ -287,7 +287,7 @@ bool RequestParser::parseFormData(const QByteArray &data)
const QString headers = QString::fromLatin1(list[0]); const QString headers = QString::fromLatin1(list[0]);
const QByteArray payload = viewWithoutEndingWith(list[1], CRLF); const QByteArray payload = viewWithoutEndingWith(list[1], CRLF);
QStringMap headersMap; HeaderMap headersMap;
const QVector<QStringRef> headerLines = headers.splitRef(CRLF, QString::SkipEmptyParts); const QVector<QStringRef> headerLines = headers.splitRef(CRLF, QString::SkipEmptyParts);
for (const auto &line : headerLines) { for (const auto &line : headerLines) {
if (line.trimmed().startsWith(HEADER_CONTENT_DISPOSITION, Qt::CaseInsensitive)) { if (line.trimmed().startsWith(HEADER_CONTENT_DISPOSITION, Qt::CaseInsensitive)) {

4
src/base/http/responsebuilder.cpp

@ -35,9 +35,9 @@ void ResponseBuilder::status(const uint code, const QString &text)
m_response.status = {code, text}; m_response.status = {code, text};
} }
void ResponseBuilder::header(const QString &name, const QString &value) void ResponseBuilder::setHeader(const Header &header)
{ {
m_response.headers[name] = value; m_response.headers[header.name] = header.value;
} }
void ResponseBuilder::print(const QString &text, const QString &type) void ResponseBuilder::print(const QString &text, const QString &type)

2
src/base/http/responsebuilder.h

@ -37,7 +37,7 @@ namespace Http
{ {
public: public:
void status(uint code = 200, const QString &text = QLatin1String("OK")); void status(uint code = 200, const QString &text = QLatin1String("OK"));
void header(const QString &name, const QString &value); void setHeader(const Header &header);
void print(const QString &text, const QString &type = CONTENT_TYPE_HTML); void print(const QString &text, const QString &type = CONTENT_TYPE_HTML);
void print(const QByteArray &data, const QString &type = CONTENT_TYPE_HTML); void print(const QByteArray &data, const QString &type = CONTENT_TYPE_HTML);
void clear(); void clear();

14
src/base/http/types.h

@ -34,8 +34,6 @@
#include <QString> #include <QString>
#include <QVector> #include <QVector>
#include "base/types.h"
namespace Http namespace Http
{ {
const char METHOD_GET[] = "GET"; const char METHOD_GET[] = "GET";
@ -92,12 +90,20 @@ namespace Http
QByteArray data; QByteArray data;
}; };
struct Header
{
QString name;
QString value;
};
using HeaderMap = QMap<QString, QString>; // <Header name, Header value>
struct Request struct Request
{ {
QString version; QString version;
QString method; QString method;
QString path; QString path;
QStringMap headers; HeaderMap headers;
QHash<QString, QByteArray> query; QHash<QString, QByteArray> query;
QHash<QString, QString> posts; QHash<QString, QString> posts;
QVector<UploadedFile> files; QVector<UploadedFile> files;
@ -112,7 +118,7 @@ namespace Http
struct Response struct Response
{ {
ResponseStatus status; ResponseStatus status;
QStringMap headers; HeaderMap headers;
QByteArray content; QByteArray content;
Response(uint code = 200, const QString &text = "OK") Response(uint code = 200, const QString &text = "OK")

52
src/webui/webapplication.cpp

@ -46,6 +46,7 @@
#include "base/http/httperror.h" #include "base/http/httperror.h"
#include "base/logger.h" #include "base/logger.h"
#include "base/preferences.h" #include "base/preferences.h"
#include "base/types.h"
#include "base/utils/bytearray.h" #include "base/utils/bytearray.h"
#include "base/utils/fs.h" #include "base/utils/fs.h"
#include "base/utils/misc.h" #include "base/utils/misc.h"
@ -335,25 +336,34 @@ void WebApplication::configure()
m_domainList = pref->getServerDomains().split(';', QString::SkipEmptyParts); m_domainList = pref->getServerDomains().split(';', QString::SkipEmptyParts);
std::for_each(m_domainList.begin(), m_domainList.end(), [](QString &entry) { entry = entry.trimmed(); }); std::for_each(m_domainList.begin(), m_domainList.end(), [](QString &entry) { entry = entry.trimmed(); });
m_isClickjackingProtectionEnabled = pref->isWebUiClickjackingProtectionEnabled();
m_isCSRFProtectionEnabled = pref->isWebUiCSRFProtectionEnabled(); m_isCSRFProtectionEnabled = pref->isWebUiCSRFProtectionEnabled();
m_isSecureCookieEnabled = pref->isWebUiSecureCookieEnabled(); m_isSecureCookieEnabled = pref->isWebUiSecureCookieEnabled();
m_isHostHeaderValidationEnabled = pref->isWebUIHostHeaderValidationEnabled(); m_isHostHeaderValidationEnabled = pref->isWebUIHostHeaderValidationEnabled();
m_isHttpsEnabled = pref->isWebUiHttpsEnabled(); m_isHttpsEnabled = pref->isWebUiHttpsEnabled();
m_contentSecurityPolicy = m_prebuiltHeaders.clear();
m_prebuiltHeaders.push_back({QLatin1String(Http::HEADER_X_XSS_PROTECTION), QLatin1String("1; mode=block")});
m_prebuiltHeaders.push_back({QLatin1String(Http::HEADER_X_CONTENT_TYPE_OPTIONS), QLatin1String("nosniff")});
if (!m_isAltUIUsed)
m_prebuiltHeaders.push_back({QLatin1String(Http::HEADER_REFERRER_POLICY), QLatin1String("same-origin")});
const bool isClickjackingProtectionEnabled = pref->isWebUiClickjackingProtectionEnabled();
if (isClickjackingProtectionEnabled)
m_prebuiltHeaders.push_back({QLatin1String(Http::HEADER_X_FRAME_OPTIONS), QLatin1String("SAMEORIGIN")});
const QString contentSecurityPolicy =
(m_isAltUIUsed (m_isAltUIUsed
? QLatin1String("") ? QLatin1String("")
: QLatin1String("default-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; script-src 'self' 'unsafe-inline'; object-src 'none'; form-action 'self';")) : QLatin1String("default-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; script-src 'self' 'unsafe-inline'; object-src 'none'; form-action 'self';"))
+ (m_isClickjackingProtectionEnabled ? QLatin1String(" frame-ancestors 'self';") : QLatin1String("")) + (isClickjackingProtectionEnabled ? QLatin1String(" frame-ancestors 'self';") : QLatin1String(""))
+ (m_isHttpsEnabled ? QLatin1String(" upgrade-insecure-requests;") : QLatin1String("")); + (m_isHttpsEnabled ? QLatin1String(" upgrade-insecure-requests;") : QLatin1String(""));
if (!contentSecurityPolicy.isEmpty())
m_prebuiltHeaders.push_back({QLatin1String(Http::HEADER_CONTENT_SECURITY_POLICY), contentSecurityPolicy});
m_useCustomHTTPHeaders = pref->isWebUICustomHTTPHeadersEnabled(); if (pref->isWebUICustomHTTPHeadersEnabled()) {
m_customHTTPHeaders.clear();
if (m_useCustomHTTPHeaders) {
const QString customHeaders = pref->getWebUICustomHTTPHeaders().trimmed(); const QString customHeaders = pref->getWebUICustomHTTPHeaders().trimmed();
const QVector<QStringRef> customHeaderLines = customHeaders.splitRef('\n', QString::SkipEmptyParts); const QVector<QStringRef> customHeaderLines = customHeaders.splitRef('\n', QString::SkipEmptyParts);
m_customHTTPHeaders.reserve(customHeaderLines.size());
for (const QStringRef &line : customHeaderLines) { for (const QStringRef &line : customHeaderLines) {
const int idx = line.indexOf(':'); const int idx = line.indexOf(':');
@ -365,7 +375,7 @@ void WebApplication::configure()
const QString header = line.left(idx).trimmed().toString(); const QString header = line.left(idx).trimmed().toString();
const QString value = line.mid(idx + 1).trimmed().toString(); const QString value = line.mid(idx + 1).trimmed().toString();
m_customHTTPHeaders.push_back({header, value}); m_prebuiltHeaders.push_back({header, value});
} }
} }
} }
@ -391,7 +401,7 @@ void WebApplication::sendFile(const QString &path)
const auto it = m_translatedFiles.constFind(path); const auto it = m_translatedFiles.constFind(path);
if ((it != m_translatedFiles.constEnd()) && (lastModified <= it->lastModified)) { if ((it != m_translatedFiles.constEnd()) && (lastModified <= it->lastModified)) {
print(it->data, it->mimeType); print(it->data, it->mimeType);
header(Http::HEADER_CACHE_CONTROL, getCachingInterval(it->mimeType)); setHeader({Http::HEADER_CACHE_CONTROL, getCachingInterval(it->mimeType)});
return; return;
} }
@ -423,7 +433,7 @@ void WebApplication::sendFile(const QString &path)
} }
print(data, mimeType.name()); print(data, mimeType.name());
header(Http::HEADER_CACHE_CONTROL, getCachingInterval(mimeType.name())); setHeader({Http::HEADER_CACHE_CONTROL, getCachingInterval(mimeType.name())});
} }
Http::Response WebApplication::processRequest(const Http::Request &request, const Http::Environment &env) Http::Response WebApplication::processRequest(const Http::Request &request, const Http::Environment &env)
@ -460,22 +470,8 @@ Http::Response WebApplication::processRequest(const Http::Request &request, cons
print(error.message(), Http::CONTENT_TYPE_TXT); print(error.message(), Http::CONTENT_TYPE_TXT);
} }
header(QLatin1String(Http::HEADER_X_XSS_PROTECTION), QLatin1String("1; mode=block")); for (const Http::Header &prebuiltHeader : asConst(m_prebuiltHeaders))
header(QLatin1String(Http::HEADER_X_CONTENT_TYPE_OPTIONS), QLatin1String("nosniff")); setHeader(prebuiltHeader);
if (m_isClickjackingProtectionEnabled)
header(QLatin1String(Http::HEADER_X_FRAME_OPTIONS), QLatin1String("SAMEORIGIN"));
if (!m_isAltUIUsed)
header(QLatin1String(Http::HEADER_REFERRER_POLICY), QLatin1String("same-origin"));
if (!m_contentSecurityPolicy.isEmpty())
header(QLatin1String(Http::HEADER_CONTENT_SECURITY_POLICY), m_contentSecurityPolicy);
if (m_useCustomHTTPHeaders) {
for (const CustomHTTPHeader &customHeader : asConst(m_customHTTPHeaders))
header(customHeader.name, customHeader.value);
}
return response(); return response();
} }
@ -567,7 +563,7 @@ void WebApplication::sessionStart()
QByteArray cookieRawForm = cookie.toRawForm(); QByteArray cookieRawForm = cookie.toRawForm();
if (m_isCSRFProtectionEnabled) if (m_isCSRFProtectionEnabled)
cookieRawForm.append("; SameSite=Strict"); cookieRawForm.append("; SameSite=Strict");
header(Http::HEADER_SET_COOKIE, cookieRawForm); setHeader({Http::HEADER_SET_COOKIE, cookieRawForm});
} }
void WebApplication::sessionEnd() void WebApplication::sessionEnd()
@ -581,7 +577,7 @@ void WebApplication::sessionEnd()
delete m_sessions.take(m_currentSession->id()); delete m_sessions.take(m_currentSession->id());
m_currentSession = nullptr; m_currentSession = nullptr;
header(Http::HEADER_SET_COOKIE, cookie.toRawForm()); setHeader({Http::HEADER_SET_COOKIE, cookie.toRawForm()});
} }
bool WebApplication::isCrossSiteRequest(const Http::Request &request) const bool WebApplication::isCrossSiteRequest(const Http::Request &request) const

11
src/webui/webapplication.h

@ -151,19 +151,10 @@ private:
// security related // security related
QStringList m_domainList; QStringList m_domainList;
bool m_isClickjackingProtectionEnabled;
bool m_isCSRFProtectionEnabled; bool m_isCSRFProtectionEnabled;
bool m_isSecureCookieEnabled; bool m_isSecureCookieEnabled;
bool m_isHostHeaderValidationEnabled; bool m_isHostHeaderValidationEnabled;
bool m_isHttpsEnabled; bool m_isHttpsEnabled;
QString m_contentSecurityPolicy;
// Custom HTTP headers QVector<Http::Header> m_prebuiltHeaders;
struct CustomHTTPHeader
{
QString name;
QString value;
};
bool m_useCustomHTTPHeaders;
QVector<CustomHTTPHeader> m_customHTTPHeaders;
}; };

Loading…
Cancel
Save