我遇到了这个错误(在生产中) - 我根据来自整个互联网和 AI 聊天机器人的响应更改了大约 20 个版本的 csp 标头,所以我对标头格式变得绝望:
Refused to load the script 'https://www.googletagmanager.com/gtm.js?id=GTM-{key}' because it violates the following Content Security Policy directive: "script-src 'self' 'unsafe-inline' 'unsafe-eval' blob:". Note that 'script-src-elem' was not explicitly set, so 'script-src' is used as a fallback.
我在中间件和下一个配置文件中编辑了以下 csp 标头的多个版本:
headers: [
{
key: 'Content-Security-Policy',
value: `
default-src 'self';
script-src 'self' 'unsafe-inline' 'unsafe-eval' https://*.googletagmanager.com https://*.google-analytics.com https://*.cloudflareinsights.com;
script-src-elem 'self' 'unsafe-inline' https://*.googletagmanager.com https://*.google-analytics.com https://*.cloudflareinsights.com;
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
img-src 'self' blob: data: https://*.google-analytics.com https://*.googletagmanager.com https://*.cloudflareinsights.com;
font-src 'self' data: https://fonts.gstatic.com;
connect-src 'self' https://*.google-analytics.com https://*.analytics.google.com https://*.googletagmanager.com https://*.cloudflareinsights.com;
frame-src 'self' https://*.googletagmanager.com;
worker-src 'self' blob:;
child-src 'self' blob:;
object-src 'none';
base-uri 'self';
form-action 'self';
frame-ancestors 'none';
upgrade-insecure-requests;
`.replace(/\s{2,}/g, ' ').trim(),
},
],
但没有任何效果(即使有克劳德和chatgpt的建议)。 这是我的 layouts.jx 文件:
//other imports
import { GoogleTagManager } from "@next/third-parties/google";
export default async function RootLayout({ children }) {
const headersList = headers();
// Get user data from headers
const nonce = headersList?.get("x-nonce") || "";
return (
<html lang="en">
<GoogleTagManager gtmId="GTM-(Actual key)" nonce={nonce} />
<body className={bodyClassName}>
<noscript>
<iframe
src={`https://www.googletagmanager.com/ns.html?id=(Actual key)`}
height="0"
width="0"
style={{ display: "none", visibility: "hidden" }}
/>
</noscript>
<div className="wrapper ovh mm-page mm-slideout">
<div>
{children}
</div>
</div>
</body>
</html>
);
}
关于随机数,这是我将其存储在中间件中的方式:
async function generateNonce() {
const array = new Uint8Array(16);
crypto.getRandomValues(array);
return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');
}
function applyHeaders(response) {
const nonce = generateNonce();
const securityHeaders = {
'Cache-Control': 'no-store, no-cache, must-revalidate, proxy-revalidate',
'Pragma': 'no-cache',
'Expires': '0',
'Surrogate-Control': 'no-store',
'X-Frame-Options': 'DENY',
'X-Content-Type-Options': 'nosniff',
'Referrer-Policy': 'strict-origin-when-cross-origin',
'Permissions-Policy': 'camera=(), microphone=(), geolocation=()',
'X-XSS-Protection': '1; mode=block',
'Content-Security-Policy': `
default-src 'self';
script-src 'self' 'unsafe-inline' 'unsafe-eval' https://*.googletagmanager.com https://*.google-analytics.com https://*.cloudflareinsights.com;
script-src-elem 'self' 'unsafe-inline' https://*.googletagmanager.com https://*.google-analytics.com https://*.cloudflareinsights.com;
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
img-src 'self' blob: data: https://*.google-analytics.com https://*.googletagmanager.com https://*.cloudflareinsights.com;
font-src 'self' data: https://fonts.gstatic.com;
connect-src 'self' https://*.google-analytics.com https://*.analytics.google.com https://*.googletagmanager.com https://*.cloudflareinsights.com;
frame-src 'self' https://*.googletagmanager.com;
worker-src 'self' blob:;
child-src 'self' blob:;
object-src 'none';
base-uri 'self';
form-action 'self';
frame-ancestors 'none';
upgrade-insecure-requests;
`.replace(/\s{2,}/g, ' ').trim()
};
Object.entries(securityHeaders).forEach(([key, value]) => {
response.headers.set(key, value);
});
response.headers.set('x-nonce', nonce)
return response;
}
此页面是您的指南:https://developers.google.com/tag-platform/security/guides/csp。
请注意,如果您将 Google Analytics 与 Signal 结合使用,您可能需要添加大量 google 域,只需点击链接即可。如果您想支持所有这些(所有国家/地区),您可能需要向 img-src 和 connect-src 添加 187 个域。