diff options
author | Michael Meeks <michael.meeks@collabora.com> | 2020-01-14 16:56:01 +0000 |
---|---|---|
committer | Jan Holesovsky <kendy@collabora.com> | 2020-01-15 10:56:27 +0100 |
commit | b5f58fbe8c4f283d10def377c8a687245312fa1f (patch) | |
tree | e9602c2fd6a04eb8aa489d7cb56428d2acb65575 | |
parent | 0a35e432ff17b1a80065c36d56069393bfba6bea (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.m4 | 2 | ||||
-rw-r--r-- | loleaflet/src/map/handler/Map.WOPI.js | 54 | ||||
-rw-r--r-- | wsd/FileServer.cpp | 28 |
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(); |