summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Meeks <michael.meeks@collabora.com>2020-01-14 16:56:01 +0000
committerJan Holesovsky <kendy@collabora.com>2020-01-15 10:56:27 +0100
commitb5f58fbe8c4f283d10def377c8a687245312fa1f (patch)
treee9602c2fd6a04eb8aa489d7cb56428d2acb65575
parent0a35e432ff17b1a80065c36d56069393bfba6bea (diff)
postMessage CSS checking improvements.
Ironically our attempts to double-check message origin against our parent was blowing a security exception. Instead send the list of origins we will accept from WSD, and check them ourselves (as well as the browser check). Why make it so hard to check that a postMessage comes from an ancestor frame ? Change-Id: I1311be3e1d68a31cfdc96b45a5eb5dd7f26e7ea9 Reviewed-on: https://gerrit.libreoffice.org/c/online/+/86788 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice@gmail.com> Reviewed-by: Alexandru Vlăduţu <alexandru.vladutu@1and1.ro> Reviewed-by: Jan Holesovsky <kendy@collabora.com>
-rw-r--r--loleaflet/html/loleaflet.html.m42
-rw-r--r--loleaflet/src/map/handler/Map.WOPI.js54
-rw-r--r--wsd/FileServer.cpp28
3 files changed, 68 insertions, 16 deletions
diff --git a/loleaflet/html/loleaflet.html.m4 b/loleaflet/html/loleaflet.html.m4
index ad6d90f4b..4addf8955 100644
--- a/loleaflet/html/loleaflet.html.m4
+++ b/loleaflet/html/loleaflet.html.m4
@@ -231,6 +231,7 @@ ifelse(MOBILEAPP,[true],
window.idleTimeoutSecs = 1000000;
window.reuseCookies = '';
window.protocolDebug = false;
+ window.frameAncestors = '';
window.tileSize = 256;],
[window.host = '%HOST%';
window.serviceRoot = '%SERVICE_ROOT%';
@@ -242,6 +243,7 @@ ifelse(MOBILEAPP,[true],
window.idleTimeoutSecs = %IDLE_TIMEOUT_SECS%;
window.reuseCookies = '%REUSE_COOKIES%';
window.protocolDebug = %PROTOCOL_DEBUG%;
+ window.frameAncestors = '%FRAME_ANCESTORS%';
window.tileSize = 256;])
syscmd([cat ]GLOBAL_JS)dnl
</script>
diff --git a/loleaflet/src/map/handler/Map.WOPI.js b/loleaflet/src/map/handler/Map.WOPI.js
index dcd401407..89f01ec53 100644
--- a/loleaflet/src/map/handler/Map.WOPI.js
+++ b/loleaflet/src/map/handler/Map.WOPI.js
@@ -141,13 +141,61 @@ L.Map.WOPI = L.Handler.extend({
this._map.fire('postMessage', {msgId: 'App_LoadingStatus', args: {Status: 'Document_Loaded', DocumentLoadedTime: this.DocumentLoadedTime}});
},
- _postMessageListener: function(e) {
+ // Naturally we set a CSP to catch badness, but check here as well.
+ // Checking whether a message came from our iframe's parents is
+ // un-necessarily difficult.
+ _allowMessageOrigin: function(e) {
+ // cache - to avoid regexps.
+ if (this._cachedGoodOrigin && this._cachedGoodOrigin === e.origin)
+ return true;
// e.origin === 'null' when sandboxed (i.e. when the parent is a file on local filesystem).
- if (e.origin !== 'null' && e.origin !== window.parent.origin) {
- return;
+ if (e.origin === 'null')
+ return true;
+ try {
+ if (e.origin === window.parent.origin)
+ return true;
+ } catch (secErr) { // security error de-referencing window.parent.origin.
+ }
+
+ // sent from the server
+ var i;
+ if (!this._allowedOrigins && window.frameAncestors)
+ {
+ var ancestors = window.frameAncestors.trim().split(' ');
+ this._allowedOrigins = ancestors;
+ // convert to JS regexps from localhost:* to https*://localhost:.*
+ for (i = 0; i < ancestors.length; i++) {
+ this._allowedOrigins[i] = 'https*://' + ancestors[i].replace(/:\*/, ':.*');
+ }
+ }
+
+ if (this._allowedOrigins)
+ {
+ for (i = 0; i < this._allowedOrigins.length; i++) {
+ if (e.origin.match(this._allowedOrigins[i]))
+ {
+ this._cachedGoodOrigin = e.origin;
+ return true;
+ }
+ }
}
+ // chrome only
+ if (window.location.ancestorOrigins &&
+ window.location.ancestorOrigins.contains(e.origin))
+ {
+ this._cachedGoodOrigin = e.origin;
+ return true;
+ }
+
+ return false;
+ },
+
+ _postMessageListener: function(e) {
+ if (!this._allowMessageOrigin(e))
+ return;
+
var msg;
try {
msg = JSON.parse(e.data);
diff --git a/wsd/FileServer.cpp b/wsd/FileServer.cpp
index 3cf3fbc51..07ac439b3 100644
--- a/wsd/FileServer.cpp
+++ b/wsd/FileServer.cpp
@@ -699,19 +699,6 @@ void FileServerRequestHandler::preprocessFile(const HTTPRequest& request, Poco::
const std::string mimeType = "text/html";
- std::ostringstream oss;
- oss << "HTTP/1.1 200 OK\r\n"
- "Date: " << Util::getHttpTimeNow() << "\r\n"
- "Last-Modified: " << Util::getHttpTimeNow() << "\r\n"
- "User-Agent: " << WOPI_AGENT_STRING << "\r\n"
- "Cache-Control:max-age=11059200\r\n"
- "ETag: \"" LOOLWSD_VERSION_HASH "\"\r\n"
- "Content-Length: " << preprocess.size() << "\r\n"
- "Content-Type: " << mimeType << "\r\n"
- "X-Content-Type-Options: nosniff\r\n"
- "X-XSS-Protection: 1; mode=block\r\n"
- "Referrer-Policy: no-referrer\r\n";
-
// Document signing: if endpoint URL is configured, whitelist that for
// iframe purposes.
std::ostringstream cspOss;
@@ -755,6 +742,7 @@ void FileServerRequestHandler::preprocessFile(const HTTPRequest& request, Poco::
//(it's deprecated anyway and CSP works in all major browsers)
cspOss << "img-src 'self' data: " << frameAncestors << "; "
<< "frame-ancestors " << frameAncestors;
+ Poco::replaceInPlace(preprocess, std::string("%FRAME_ANCESTORS%"), frameAncestors);
}
else
{
@@ -763,6 +751,20 @@ void FileServerRequestHandler::preprocessFile(const HTTPRequest& request, Poco::
}
cspOss << "\r\n";
+
+ std::ostringstream oss;
+ oss << "HTTP/1.1 200 OK\r\n"
+ "Date: " << Util::getHttpTimeNow() << "\r\n"
+ "Last-Modified: " << Util::getHttpTimeNow() << "\r\n"
+ "User-Agent: " << WOPI_AGENT_STRING << "\r\n"
+ "Cache-Control:max-age=11059200\r\n"
+ "ETag: \"" LOOLWSD_VERSION_HASH "\"\r\n"
+ "Content-Length: " << preprocess.size() << "\r\n"
+ "Content-Type: " << mimeType << "\r\n"
+ "X-Content-Type-Options: nosniff\r\n"
+ "X-XSS-Protection: 1; mode=block\r\n"
+ "Referrer-Policy: no-referrer\r\n";
+
// Append CSP to response headers too
oss << cspOss.str();