0

我使用新的 Google 站点(不是 Classic Sites)创建了一个站点,通过 Cloudflare 设置站点代理,并在 Cloudflare 中启用了电子邮件地址混淆功能。然后我添加了一个执行简单操作的按钮mailto:info@example.com并遇到了问题:当我单击该按钮时,我被带到 Cloudflare“电子邮件保护”页面,并显示消息“您无法访问此电子邮件地址 example.com” .

这是出于一个简单的原因 - 我的浏览器(任何现代浏览器都会发生这种情况)没有email-decode.min.js从 Cloudflare 加载脚本。反过来,这是因为 Google 协作平台使用 CSP >= v2,并且 CSP 指令的配置方式不允许加载来自 Cloudflare 的脚本。

根据 Cloudflare 文档,为了使用Scrape Shield,您需要更新 CSP 标头,如下所示:

script-src 'self' 'unsafe-inline'

这是新的 Google 协作平台 CSP 标头的样子:

base-uri 'self';
object-src 'none';
report-uri /_/view/cspreport;
script-src 'report-sample' 'nonce-7+8CsMF6KihKnNmDwfM84w' 'unsafe-inline' 'unsafe-eval';
worker-src 'self';
frame-ancestors https://google-admin.corp.google.com/

*nonce-<base64-value>随每个请求更新。

加载包含电子邮件的页面时,我在浏览器控制台中看到以下错误:

Refused to load the script 'https://example.com/cdn-cgi/scripts/6d6ddgh8/cloudflare-static/email-decode.min.js' because it violates the following Content Security Policy directive: "script-src 'report-sample' 'nonce-7+8CsMF6KihKnNmDwfM84w' 'unsafe-inline' 'unsafe-eval'". Note that 'script-src-elem' was not explicitly set, so 'script-src' is used as a fallback.

我不确定原因,但由于以下两个原因之一,脚本无法加载:

  1. 'self'没有为指令设置源script-src(我认为不是这种情况)。
  2. 'unsafe-inline'被忽略,因为存在加密随机数。

无论如何它都不起作用。我看到了两个解决方案,但我不知道如何实现它们:

  1. 对于第一个原因(尽管我不认为是这种情况),如果 Google 将源添加'self'script-src指令中,或者如果我有能力自定义此标头并且我会自己做,则可以解决此问题。
  2. 'nonce-<base64-value>'出于第二个原因,如果 Cloudflare在请求站点时读取 Google 服务器返回的信息并将其添加到其脚本中,则可以解决此问题。

如果有人可以分享这个问题的解决方案,将不胜感激。

4

1 回答 1

1

所以我发现原因是指令中缺少'self'源。script-src

我发现一个论坛帖子建议使用Cloudflare Workers即时更改请求/响应中所需的数据。我还为允许替换请求/响应中的标头的工作人员找到了一个现成的示例代码。

受这个想法的启发,鉴于 Cloudflare 每天免费提供 100,000 个请求,我编写并部署了一个更改服务器响应标头的工作代码,实际上,它更新了标头script-src中的指令content-security-policy,并用变量中指定的源来补充它sources.

我的问题解决了,现在 Cloudflare 脚本正在加载并且按钮正在工作。

* 我不保证代码质量,但它确实有效。代码可以变得更加通用,但我没有时间去做。

这是我的工人代码:

addEventListener("fetch", event => {
  event.respondWith(handleRequest(event.request))
})

const sources = ["'self'"]

/**
 * The function to update a CSP directive with new sources.
 * @param {string} directive CSP directive.
 * @param {string[]} sources Sources to add to the directive.
 * @return {string} Updated CSP directive.
 */
 function updateDirective(directive, sources) {
  for (let i = 0; i < sources.length; i++) {
    if (!directive.toLowerCase().includes(sources[i])) {
      directive = directive.concat(" ", sources[i])
    }
  }
  
  return directive
}

/**
 * The function to update the Content-Security-Policy header.
 * @param {string} header The Content-Security-Policy header.
 * @param {string} directive The Content-Security-Policy directive whose sources need to be updated.
 * @param {string} sources Sources to add to the directive.
 * @return {string} Updated Content-Security-Policy header.
 */
function updateHeader(header, directive, sources) {
  let sourceHeader = header.split(';')
  let updatedHeader = []
  
  for (let i = 0; i < sourceHeader.length; i++) {
    if (sourceHeader[i].includes(directive)) {
      updatedHeader.push(updateDirective(sourceHeader[i], sources))
    } else {
      updatedHeader.push(sourceHeader[i])
    }
  }
  
  return updatedHeader.join(";")
}

async function handleRequest(request) {
  let response = await fetch(request)

  response = new Response(response.body, response)
  response.headers.set('content-security-policy',
   updateHeader(response.headers.get('content-security-policy'), "script-src", sources))

  return response
}
于 2021-06-16T18:36:29.323 回答