Chrome扩展:使用不可信源创建图片时触发JavaScript错误
问题描述
在Chrome扩展中爬取页面并渲染结果,尝试加入网站图标(favicon)时,触发了如下Content Security Policy(CSP)错误:
Refused to load the script 'https://gohugo.io/dist/app.bundle.js' because it violates the following Content Security Policy directive: "script-src 'self'". Note that 'script-src-elem' was not explicitly set, so 'script-src' is used as a fallback.
推测部分Hugo博客的图标链接指向JS文件,但疑惑为何img标签的src会触发脚本加载。图标渲染代码如下:
try { const resultImg = document.createElement("img"); resultImg.src = result.favicon; if (result.favicon.includes(".js")) { // this never hits console.warn("found favicon that is js!"); console.dir(result); } resultImg.width = 24; resultImg.height = 24; resultImg.className = "favicon"; return resultImg } catch (err) { return document.createElement("span") }
注释该代码后错误消失,确认由其导致,但try-catch未捕获错误。另外发现Chrome访问https://gohugo.io/favicon-32x32.png时会加载展示图片的HTML页面,而curl直接下载PNG,怀疑存在含JS的图标链接。
问题分析与解决
核心原因
1. 为什么img标签触发脚本加载?
根源是服务器的内容协商机制:Chrome请求图标时,请求头的Accept字段包含text/html,服务器返回了包裹图片的HTML页面,而非直接返回PNG文件。这个HTML里嵌入了app.bundle.js脚本,浏览器解析HTML资源时会尝试加载脚本,触发CSP限制。而curl的Accept是*/*,服务器直接返回图片,所以不会触发脚本加载。
2. 为什么try-catch没捕获错误?
CSP错误属于浏览器级别的安全限制,这类错误不会抛出到JavaScript执行上下文,因此try-catch无法捕获,只会在控制台输出。
解决方案
方案1:过滤非图片格式的链接
直接排除非图片后缀的链接,只加载.png、.ico、.svg等格式的资源:
const validImageExts = [".png", ".ico", ".svg", ".jpg", ".jpeg"]; const isImageFavicon = validImageExts.some(ext => result.favicon.endsWith(ext)); if (!isImageFavicon) { return document.createElement("span"); } // 后续正常创建img标签
方案2:强制请求图片资源
通过修改请求参数或设置属性,让服务器返回图片而非HTML:
const resultImg = document.createElement("img"); // 添加缓存破折号或格式参数,强制服务器返回图片 resultImg.src = `${result.favicon}?t=${Date.now()}`; resultImg.crossOrigin = "anonymous"; // 监听加载失败事件,降级处理 resultImg.onerror = () => { resultImg.replaceWith(document.createElement("span")); }; resultImg.width = 24; resultImg.height = 24; resultImg.className = "favicon"; return resultImg;
方案3:提前验证资源类型
用fetch发送HEAD请求,检查响应的Content-Type是否为图片类型:
async function createFaviconElem(url) { try { const resp = await fetch(url, { method: "HEAD" }); const contentType = resp.headers.get("Content-Type"); if (!contentType?.startsWith("image/")) { return document.createElement("span"); } const img = document.createElement("img"); img.src = url; img.width = 24; img.height = 24; img.className = "favicon"; return img; } catch { return document.createElement("span"); } } // 调用示例(需在async函数中) // const faviconElem = await createFaviconElem(result.favicon);
内容的提问来源于stack exchange,提问作者user717847
相关产品推荐
相关产品推荐

