在看似有效的网址上违反了内容安全政策

时间:2019-09-10 13:50:30

标签: html image service-worker content-security-policy

我有一个网站正在从openstreetmap加载图像图块。 这些图像是从openstreetmap.org的多个子域提供的,例如:

  • a.tile.openstreetmap.org
  • b.tile.openstreetmap.org
  • c.tile.openstreetmap.org

为此,我在内容安全政策img-src中添加了*.openstreetmap.org,以便所有子域均有效。

完整的img-src现在显示为:

img-src https: data: *.openstreetmap.org;

出于完整性考虑,完整的Content-Security-Policy为:

default-src 'self'; script-src https: 'unsafe-inline' 'unsafe-eval'; style-src https: 'unsafe-inline'; img-src https: data: *.openstreetmap.org;

这似乎在网站首次加载时正常运行,并且包含地图的页面被导航到。实际上,有多个页面包含地图,并且都可以使用。

但是,当用户单击“刷新”时,会发生以下错误:

Refused to connect to 'https://a.tile.openstreetmap.org/12/2011/1356.png' because it violates the document's Content Security Policy.
GET https://a.tile.openstreetmap.org/12/2010/1355.png net::ERR_FAILED

如果我打开Chrome开发工具并使用“右键单击刷新”->“清空缓存并进行硬重载”选项来重载页面,则图像图块会再次加载,但是只有随后的“正常”刷新才会产生同样的错误。

此站点使用的是“ serviceworker.js”,本地服务器页面已缓存,但openstreetmap.org中的图块未缓存。但是,我已手动清空此缓存,并验证了缓存的版本具有正确的Content-Security-Policy,因此,serviceworker.js的存在可能是用词不当。

编辑1

发布问题使我进一步调查了serviceworker.js。我删除了它,问题不再存在。恢复服务人员后,问题再次出现。这就是原因。

openstreetmap.org在应用程序缓存中没有任何内容。

编辑2

进一步的调查已找到原因,但离解决方案还很近。当我的“ serviceworker.js”基于缓存优先,然后基于网络进行操作时,第一个负载就起作用了。因此,第一次加载没有缓存。后续加载中有一个缓存,但是openstreetmap.org中的图像不在其中。

相关部分是:

self.addEventListener('fetch', function (event) {
    event.respondWith(
        // cache first then network
        // but not for different origin calls, this includes API
        caches.open("my-cache-" + self.sw_version)
            .catch((msg) => { console.log("caches.open(): ", msg); })
            .then(cache => {

                return cache.match(event.request)
                    .catch((msg) => { console.log("cache.match(): ", msg); })
                    .then(response => {

                        return response || fetch(event.request)
                            .catch((msg) => { console.log("fetch(event.request): ", msg); })
                            .then(response => {

                            // if it was from a differnt origin - simply return it
                            if (!event.request.url.match(event.request.referrer))
                                return response;

                            console.log("ServiceWorker.caching: ", event.request.url);
                            cache.put(event.request, response.clone());
                            return response;
                    });
                });
        })
    );
});

已将问题确定为服务工作者在缓存中找不到请求后发出fetch()请求。

fetch(event.request):  TypeError: Failed to fetch

这暗示serviceworker.js中的fetch()使用的不是正确的Content-Security-Policy,而是使用了请求所违反的内容。

开发人员工具的“网络”标签显示了此请求/响应。

请求

Request URL: https://b.tile.openstreetmap.org/12/2011/1355.png
Referrer Policy: no-referrer-when-downgrade

响应

Origin: https://localhost:9443
Referer: https://localhost:9443/my-app/?
Sec-Fetch-Mode: cors
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36

编辑3

通过显式设置来强制fetch()使用相同的Content-Security-Policy,以允许请求正常工作。但这是一个hackey修复程序,因为它现在在两个地方(整个网站,以及在serviceworker.js中)声明,因此,如果有一个“适当的”修复程序,或者只是一个解释为什么需要这样做,我仍然会感兴趣。

编辑4

我今天又回到这个话题。 我意识到检查远程URL时出现问题-我将引荐来源网址传递给未转义的match函数,因此它的行为不符合预期。我现在已经更正了这一点,并移动了一些代码以使它(对我自己)更加清晰。

self.addEventListener('fetch', function (event) {

    // https://stackoverflow.com/a/6969486 - escape a string to use in regex
    if (!event.request.url.match(event.request.referrer.replace(/[-[\]/{}()*+?.\\^$|]/g, '\\$&'))) {
        fetch(event.request)
            .catch((msg) => { console.log("fetch(event.request).remoteorigin: ", { event: event, error: msg }); })
            .then(response => {
                return response;
            });
    }
    else {
        event.respondWith(
            // cache first then network
            // but not for different origin calls, this includes API
            caches.open("crt-scada-" + self.sw_version)
                .catch((msg) => { console.log("caches.open(): ", msg); })
                .then(cache => {
                    return cache.match(event.request)
                        .catch((msg) => { console.log("cache.match(): ", msg); })
                        .then(response => {
                            return response || fetch(event.request)
                                .catch((msg) => { console.log("fetch(event.request).sameorigin: ", { event: event, error: msg }); })
                                .then(response => {
                                    cache.put(event.request, response.clone());
                                    return response;
                                });

                        });
                })
        );
    }
});

现在,我遇到了奇怪的情况,即它报告了相同的错误-而且还从openstreetmap中加载了图块。

对于为什么会发生这种情况的任何建议,我们将不胜感激。

0 个答案:

没有答案