Html 在看似有效的URL上违反内容安全策略
我有一个网站正在从openstreetmap加载图像块。 这些图像来自openstreetmap.org的多个子域,例如:Html 在看似有效的URL上违反内容安全策略,html,image,service-worker,content-security-policy,Html,Image,Service Worker,Content Security Policy,我有一个网站正在从openstreetmap加载图像块。 这些图像来自openstreetmap.org的多个子域,例如: a.tile.openstreetmap.org b.tile.openstreetmap.org c.tile.openstreetmap.org 为了允许这样做,我在我的内容安全策略*.openstreetmap.org中添加了一个img src,这样所有子域都应该是有效的 完整的img src现在显示: img-src https: data: *.openstr
a.tile.openstreetmap.org
b.tile.openstreetmap.org
c.tile.openstreetmap.org
*.openstreetmap.org
中添加了一个img src
,这样所有子域都应该是有效的
完整的img src
现在显示:
img-src https: data: *.openstreetmap.org;
为了完整性起见,完整内容安全策略为:
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
中的平铺没有缓存。然而,我已经手动清空了这个缓存,并验证了缓存版本具有正确的内容安全策略,因此可能存在serviceworker.js
是一个误称
编辑1
发布这个问题让我进一步调查了serviceworner.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()
没有使用正确的内容安全策略,而是使用了请求违反的策略
dev工具的network选项卡为请求/响应显示了这一点
请求
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()
使用相同的内容安全策略,允许请求工作。但这是一个黑客补丁,正如它现在在两个地方(整个网站和serviceworker.js中)声明的那样,因此如果有一个“合适的”补丁,甚至只是解释为什么需要这样做,我仍然会感兴趣
编辑4
我今天回到这里是想再尝试一次。
我意识到在检查远程URL时出现了一个问题——我正在将referer URL传递给match函数unscaped,因此它的行为不符合预期。我现在已经纠正了这一点,并移动了一些代码以使它(对我自己)更清晰
现在我遇到了一个奇怪的情况,它报告了相同的错误,但也从openstreetmap加载了瓷砖
任何关于为什么会发生这种情况的建议都将被感激地接受。强制在每个请求上显式声明标题似乎会导致其他fetch()出现各种问题,因此我不得不将其删除。仍然在寻找解决方案/解释有一点:我有类似的东西,尽管我对软件线程使用相同的内容安全策略。该链接指出,当sw使用fetch()时,它使用connect src,这可能值得一试,尽管对我来说,更改错误的人并没有修复它。
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;
});
});
})
);
}
});