内容安全策略

什么是 CSP?

内容安全策略(CSP) 是一种增加的安全层,有助于检测和缓解某些类型的攻击,包括跨站脚本(XSS)和数据注入攻击。这些攻击可用于从数据盗窃到网站篡改,再到恶意软件分发。

集成步骤

添加路由插件

要将中间件插件添加到您的应用程序中,只需在 routes 文件夹中添加一个名为 plugin@your-plugin-name.ts 的文件。 该文件将在每个请求中加载,允许您添加标头、修改响应等。

添加插件
src/
└── routes/
    ├── plugin@csp.ts         # 在每个请求上运行的插件(路由中间件)
    ├── contact/
       └── index.mdx         # https://example.com/contact
    ├── about/
       └── index.md          # https://example.com/about
    ├── index.mdx             # https://example.com/
    
    └── layout.tsx            # 用于所有页面的布局

示例

此模板提供了非常宽松的向后兼容默认值。 强烈建议您根据自己的特定用例进行自定义。 由于这是一个高级主题,您应该仔细阅读 MDNweb.dev,以更好地理解 CSP。 请注意,在开发模式下,Vite 脚本没有 nonce,并且会报告错误。因此,示例不会在开发模式下添加 csp。

src/routes/plugin@csp.ts
import type { RequestHandler } from "@builder.io/qwik-city";
import { isDev } from "@builder.io/qwik/build";
 
export const onRequest: RequestHandler = event => {
  if (isDev) return; // 在开发模式下不返回 CSP 标头
  const nonce = Date.now().toString(36); // 在此处添加您的自定义 nonce 逻辑
  event.sharedMap.set("@nonce", nonce);
  const csp = [
    `default-src 'self' 'unsafe-inline'`,
    `font-src 'self'`,
    `img-src 'self' 'unsafe-inline' data:`,
    `script-src 'self' 'unsafe-inline' https: 'nonce-${nonce}' 'strict-dynamic'`,
    `style-src 'self' 'unsafe-inline'`,
    `frame-src 'self' 'nonce-${nonce}'`,
    `object-src 'none'`,
    `base-uri 'self'`,
  ];
 
  event.headers.set("Content-Security-Policy", csp.join("; "));
};

同样添加到 Service Worker

src/root.ts
import { component$, useServerData } from "@builder.io/qwik";
import {
  QwikCityProvider,
  RouterOutlet,
  ServiceWorkerRegister,
} from "@builder.io/qwik-city";
import { RouterHead } from "./components/router-head/router-head";
 
import "./global.css";
 
export default component$(() => {
  const nonce = useServerData<string | undefined>("nonce");
  return (
    <QwikCityProvider>
      <head>
        <meta charSet="utf-8" />
        <link rel="manifest" href="/manifest.json" />
        <RouterHead />
      </head>
      <body lang="en">
        <RouterOutlet />
        <ServiceWorkerRegister nonce={nonce} />
      </body>
    </QwikCityProvider>
  );
});

自定义脚本

如果您有需要添加 nonce 的自定义脚本标签,您可以使用 useServerData 钩子从服务器获取 nonce,并将其添加到您的脚本标签中。

src/components/some-component.tsx
export default component$(() => {
  const nonce = useServerData<string | undefined>("nonce");
  return (
    <div>
      <script nonce={nonce}>alert("Hello world")</script>
    </div>
  );
});

验证您的 CSP

有一个很好的工具可以验证您的 CSP:https://csp-evaluator.withgoogle.com/

Contributors

Thanks to all the contributors who have helped make this documentation better!

  • tzdesign
  • jordanw66
  • hamatoyogi