Skip to content

安全配置指南

本文档介绍 VitePress 站点的安全配置最佳实践,帮助你保护站点和用户数据。

内容安全策略 (CSP)

什么是 CSP?

内容安全策略(Content Security Policy)是一种 HTTP 头,用于防止 XSS 攻击和数据注入。

配置 CSP

方式一:HTTP 响应头

在部署平台配置:

nginx
# Nginx 配置
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https:; style-src 'self' 'unsafe-inline' https:; img-src 'self' data: https:; font-src 'self' https:; connect-src 'self' https:; frame-ancestors 'self';";
yaml
# Vercel - vercel.json
{
  "headers": [
    {
      "source": "/(.*)",
      "headers": [
        {
          "key": "Content-Security-Policy",
          "value": "default-src 'self'; script-src 'self' 'unsafe-inline' https:; style-src 'self' 'unsafe-inline' https:; img-src 'self' data: https:;"
        }
      ]
    }
  ]
}
yaml
# Netlify - netlify.toml
[[headers]]
  for = "/*"
  [headers.values]
    Content-Security-Policy = "default-src 'self'; script-src 'self' 'unsafe-inline' https:; style-src 'self' 'unsafe-inline' https:;"

方式二:HTML meta 标签

ts
// docs/.vitepress/config.mts
export default defineConfig({
  head: [
    [
      'meta',
      {
        'http-equiv': 'Content-Security-Policy',
        'content': "default-src 'self'; script-src 'self' 'unsafe-inline' https:; style-src 'self' 'unsafe-inline' https:;"
      }
    ]
  ]
})

注意

使用 meta 标签配置 CSP 时,某些指令(如 frame-ancestors)不生效。推荐使用 HTTP 响应头。

CSP 指令说明

指令说明示例
default-src默认资源来源'self'
script-srcJavaScript 来源'self' 'unsafe-inline'
style-srcCSS 来源'self' 'unsafe-inline'
img-src图片来源'self' data: https:
font-src字体来源'self' https:
connect-srcXHR/Fetch 来源'self' https:
frame-srciframe 来源'self'
frame-ancestors允许嵌入的父页面'self'

常用 CSP 配置

基础配置

nginx
Content-Security-Policy: default-src 'self'; img-src 'self' data: https:; style-src 'self' 'unsafe-inline';

允许内联脚本(VitePress 需要)

nginx
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline';

允许第三方服务

nginx
# 允许 Google Analytics 和 Algolia
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' https://www.googletagmanager.com https://cdn.jsdelivr.net; connect-src 'self' https://*.algolia.net;

防止 XSS 攻击

什么是 XSS?

跨站脚本攻击(XSS)是一种注入攻击,攻击者在网页中插入恶意脚本。

在 Markdown 中安全使用 HTML

避免直接使用用户输入

markdown
<!-- ❌ 危险:直接使用用户输入 -->
<div v-html="userInput"></div>

<!-- ✅ 安全:使用插值表达式 -->
<div>{{ userInput }}</div>

转义 HTML 内容

vue
<script setup>
import { computed } from 'vue'

const props = defineProps({
  content: String
})

// HTML 转义函数
function escapeHtml(text) {
  const map = {
    '&': '&amp;',
    '<': '&lt;',
    '>': '&gt;',
    '"': '&quot;',
    "'": '&#039;'
  }
  return text.replace(/[&<>"']/g, m => map[m])
}

const safeContent = computed(() => escapeHtml(props.content))
</script>

<template>
  <div>{{ safeContent }}</div>
</template>

安全使用 v-html

仅在可信内容时使用 v-html

vue
<script setup>
// 仅用于信任的内容源
const trustedContent = `<strong>安全内容</strong>`
</script>

<template>
  <!-- 仅当内容来源可信时使用 -->
  <div v-html="trustedContent"></div>
</template>

使用 Vue 的安全机制

Vue 自动转义插值内容:

vue
<template>
  <!-- ✅ 自动转义 -->
  <p>{{ userInput }}</p>
  
  <!-- ❌ 需要手动确保安全 -->
  <p v-html="userInput"></p>
</template>

安全相关的 HTTP 头

X-Content-Type-Options

防止 MIME 类型嗅探:

nginx
add_header X-Content-Type-Options "nosniff";

X-Frame-Options

防止点击劫持:

nginx
add_header X-Frame-Options "SAMEORIGIN";

X-XSS-Protection

启用浏览器 XSS 过滤:

nginx
add_header X-XSS-Protection "1; mode=block";

Referrer-Policy

控制 Referer 信息:

nginx
add_header Referrer-Policy "strict-origin-when-cross-origin";

Permissions-Policy

控制浏览器功能:

nginx
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()";

完整安全头配置

nginx
# Nginx 配置示例
server {
    # 内容安全策略
    add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https:; style-src 'self' 'unsafe-inline' https:; img-src 'self' data: https:; font-src 'self' https:; connect-src 'self' https:; frame-ancestors 'self';";
    
    # 防止 MIME 类型嗅探
    add_header X-Content-Type-Options "nosniff";
    
    # 防止点击劫持
    add_header X-Frame-Options "SAMEORIGIN";
    
    # XSS 保护
    add_header X-XSS-Protection "1; mode=block";
    
    # Referrer 策略
    add_header Referrer-Policy "strict-origin-when-cross-origin";
    
    # 权限策略
    add_header Permissions-Policy "geolocation=(), microphone=(), camera=()";
}

敏感信息处理

不要在代码中存储敏感信息

ts
// ❌ 错误:硬编码敏感信息
const apiKey = 'sk-1234567890abcdef'
const dbPassword = 'my-secret-password'

// ✅ 正确:使用环境变量
const apiKey = process.env.API_KEY
const dbPassword = process.env.DB_PASSWORD

使用环境变量

ts
// docs/.vitepress/config.mts
import { defineConfig, loadEnv } from 'vitepress'

const env = loadEnv('', process.cwd())

export default defineConfig({
  head: [
    // 仅在构建时注入非敏感配置
    ...(env.VITE_GA_ID ? [
      ['script', { src: `https://www.googletagmanager.com/gtag/js?id=${env.VITE_GA_ID}` }]
    ] : [])
  ]
})

.gitignore 配置

gitignore
# 敏感文件
.env.local
.env.*.local
*.pem
*.key
secrets/

客户端敏感信息

对于必须在客户端使用的密钥(如 Algolia 搜索 Key):

ts
// ✅ 只读密钥可以在客户端使用
export default defineConfig({
  themeConfig: {
    search: {
      provider: 'algolia',
      options: {
        appId: 'PUBLIC_APP_ID',      // 公开信息
        apiKey: 'PUBLIC_SEARCH_KEY', // 只读搜索密钥,可公开
        indexName: 'my-index'
      }
    }
  }
})

注意

永远不要在客户端使用 Admin Key 或有写权限的 Key。

第三方脚本安全

审查第三方脚本

在引入第三方脚本前:

  1. 检查脚本来源是否可信
  2. 确认脚本用途
  3. 定期更新版本

使用 SRI(子资源完整性)

ts
// docs/.vitepress/config.mts
export default defineConfig({
  head: [
    [
      'script',
      {
        src: 'https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.prod.js',
        integrity: 'sha384-xxx',  // 使用 SRI 哈希
        crossorigin: 'anonymous'
      }
    ]
  ]
})

生成 SRI 哈希:

bash
# 生成 SRI 哈希
curl -s https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.prod.js | openssl dgst -sha384 -binary | openssl base64 -A

限制第三方脚本权限

使用 CSP 限制第三方脚本:

nginx
Content-Security-Policy: script-src 'self' https://cdn.jsdelivr.net https://www.googletagmanager.com;

HTTPS 配置

启用 HTTPS

所有现代站点都应使用 HTTPS:

nginx
# Nginx HTTPS 配置
server {
    listen 443 ssl http2;
    server_name example.com;
    
    ssl_certificate /path/to/cert.pem;
    ssl_certificate_key /path/to/key.pem;
    
    # SSL 优化配置
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
    ssl_prefer_server_ciphers off;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 1d;
}

# HTTP 重定向到 HTTPS
server {
    listen 80;
    server_name example.com;
    return 301 https://$server_name$request_uri;
}

HSTS(HTTP Strict Transport Security)

nginx
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

防止信息泄露

移除敏感信息

ts
// docs/.vitepress/config.mts
export default defineConfig({
  head: [
    // 移除可能泄露信息的头
    ['meta', { name: 'generator', content: '' }],  // 移除生成器标识
  ]
})

禁用目录列表

nginx
# Nginx
autoindex off;

错误页面处理

ts
// docs/.vitepress/config.mts
export default defineConfig({
  // 自定义 404 页面
  cleanUrls: true
})

创建友好的错误页面:

vue
<!-- docs/.vitepress/theme/components/Error.vue -->
<template>
  <div class="error-page">
    <h1>页面未找到</h1>
    <p>抱歉,您访问的页面不存在。</p>
    <a href="/">返回首页</a>
  </div>
</template>

安全检查清单

发布前检查以下项目:

  • [ ] 所有敏感信息使用环境变量
  • [ ] 配置了 CSP 头
  • [ ] 启用了 HTTPS
  • [ ] 配置了 X-Frame-Options
  • [ ] 配置了 X-Content-Type-Options
  • [ ] 第三方脚本使用 SRI
  • [ ] 移除了调试代码和注释中的敏感信息
  • [ ] .gitignore 包含敏感文件
  • [ ] 依赖没有已知漏洞

依赖安全审计

检查依赖漏洞

bash
# npm 审计
npm audit

# 自动修复
npm audit fix

# 检查过期依赖
npm outdated

使用 Dependabot

在 GitHub 仓库中创建 .github/dependabot.yml

yaml
version: 2
updates:
  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "weekly"
    open-pull-requests-limit: 10

安全最佳实践总结

实践说明
使用 HTTPS全站强制 HTTPS
配置 CSP防止 XSS 攻击
环境变量敏感信息不入代码
定期审计检查依赖漏洞
最小权限只暴露必要信息
及时更新保持依赖最新

相关资源

下一步

学习 调试与排错 了解如何排查问题。

贡献者

加载中...

想要成为贡献者?

在 CNB 上参与贡献