安全配置指南
本文档介绍 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-src | JavaScript 来源 | 'self' 'unsafe-inline' |
style-src | CSS 来源 | 'self' 'unsafe-inline' |
img-src | 图片来源 | 'self' data: https: |
font-src | 字体来源 | 'self' https: |
connect-src | XHR/Fetch 来源 | 'self' https: |
frame-src | iframe 来源 | '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 = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": '''
}
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。
第三方脚本安全
审查第三方脚本
在引入第三方脚本前:
- 检查脚本来源是否可信
- 确认脚本用途
- 定期更新版本
使用 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 攻击 |
| 环境变量 | 敏感信息不入代码 |
| 定期审计 | 检查依赖漏洞 |
| 最小权限 | 只暴露必要信息 |
| 及时更新 | 保持依赖最新 |
相关资源
下一步
学习 调试与排错 了解如何排查问题。