Skip to content

Commit 6cb40ad

Browse files
committed
修改 csp 策略,支持第三方资源
1 parent 84736d9 commit 6cb40ad

File tree

3 files changed

+361
-4
lines changed

3 files changed

+361
-4
lines changed
Lines changed: 357 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,357 @@
1+
# Content Security Policy (CSP) 配置指南
2+
3+
## 当前 CSP 配置
4+
5+
```
6+
Content-Security-Policy:
7+
default-src 'self';
8+
script-src 'self' 'unsafe-inline' 'unsafe-eval' https://static.cloudflareinsights.com;
9+
style-src 'self' 'unsafe-inline';
10+
img-src 'self' data: https:;
11+
font-src 'self' data:;
12+
connect-src 'self' https://cloudflareinsights.com https://favicon.im;
13+
frame-ancestors 'none';
14+
```
15+
16+
## 配置说明
17+
18+
### 1. `default-src 'self'`
19+
**默认策略**:只允许同源资源
20+
21+
### 2. `script-src`
22+
**允许的脚本来源**
23+
-`'self'` - 本站脚本
24+
-`'unsafe-inline'` - 内联脚本(Next.js 需要)
25+
-`'unsafe-eval'` - eval() 函数(某些库需要)
26+
-`https://static.cloudflareinsights.com` - Cloudflare 分析
27+
28+
**可能被阻止的**
29+
- ❌ 其他第三方脚本(如 Google Analytics、百度统计等)
30+
31+
### 3. `style-src`
32+
**允许的样式来源**
33+
-`'self'` - 本站样式
34+
-`'unsafe-inline'` - 内联样式(Ant Design、Tailwind 需要)
35+
36+
**可能被阻止的**
37+
- ❌ 外部 CDN 样式表(如 Google Fonts)
38+
39+
### 4. `img-src`
40+
**允许的图片来源**
41+
-`'self'` - 本站图片
42+
-`data:` - Base64 图片
43+
-`https:` - **所有 HTTPS 协议的图片(不限域名)**
44+
45+
**这意味着**
46+
- ✅ favicon.im 的图标
47+
- ✅ 用户自定义的任意 HTTPS 图标 URL
48+
-`https://example.com/icon.png`
49+
-`https://cdn.example.com/logo.svg`
50+
-`https://any-domain.com/image.jpg`
51+
-`http://insecure.com/icon.png`(不安全的 HTTP 会被阻止)
52+
53+
**重要**`https:` 是协议通配符,允许所有 HTTPS 来源,非常适合用户自定义图标的场景
54+
55+
### 5. `font-src`
56+
**允许的字体来源**
57+
-`'self'` - 本站字体
58+
-`data:` - Base64 字体
59+
60+
**可能被阻止的**
61+
- ❌ 外部字体 CDN(如 Google Fonts)
62+
63+
### 6. `connect-src`
64+
**允许的网络连接**
65+
-`'self'` - 本站 API
66+
-`https://cloudflareinsights.com` - Cloudflare 分析
67+
-`https://favicon.im` - Favicon API
68+
69+
**可能被阻止的**
70+
- ❌ 其他第三方 API
71+
- ❌ WebSocket 连接(如果需要)
72+
73+
### 7. `frame-ancestors`
74+
**iframe 嵌入限制**
75+
-`'none'` - 禁止被任何网站嵌入
76+
77+
## 潜在问题和解决方案
78+
79+
### 问题 1:添加第三方分析工具
80+
81+
**场景**:想添加 Google Analytics、百度统计等
82+
83+
**解决方案**
84+
```diff
85+
script-src 'self' 'unsafe-inline' 'unsafe-eval'
86+
https://static.cloudflareinsights.com
87+
+ https://www.googletagmanager.com
88+
+ https://www.google-analytics.com
89+
+ https://hm.baidu.com;
90+
91+
connect-src 'self'
92+
https://cloudflareinsights.com
93+
https://favicon.im
94+
+ https://www.google-analytics.com
95+
+ https://hm.baidu.com;
96+
```
97+
98+
### 问题 2:使用 Google Fonts
99+
100+
**场景**:想使用 Google Fonts 字体
101+
102+
**解决方案**
103+
```diff
104+
style-src 'self' 'unsafe-inline'
105+
+ https://fonts.googleapis.com;
106+
107+
font-src 'self' data:
108+
+ https://fonts.gstatic.com;
109+
```
110+
111+
### 问题 3:嵌入外部视频
112+
113+
**场景**:想嵌入 YouTube、Bilibili 视频
114+
115+
**解决方案**
116+
```diff
117+
Content-Security-Policy:
118+
default-src 'self';
119+
...
120+
+ frame-src https://www.youtube.com https://player.bilibili.com;
121+
```
122+
123+
### 问题 4:使用 CDN 加速
124+
125+
**场景**:使用 CDN 托管静态资源
126+
127+
**解决方案**
128+
```diff
129+
script-src 'self' 'unsafe-inline' 'unsafe-eval'
130+
+ https://cdn.jsdelivr.net
131+
+ https://unpkg.com;
132+
133+
style-src 'self' 'unsafe-inline'
134+
+ https://cdn.jsdelivr.net
135+
+ https://unpkg.com;
136+
```
137+
138+
### 问题 5:WebSocket 连接
139+
140+
**场景**:需要 WebSocket 实时通信
141+
142+
**解决方案**
143+
```diff
144+
connect-src 'self'
145+
https://cloudflareinsights.com
146+
https://favicon.im
147+
+ wss://your-websocket-server.com;
148+
```
149+
150+
## 当前配置的安全性
151+
152+
### ✅ 已保护的攻击
153+
154+
1. **XSS 攻击**:限制脚本来源
155+
2. **点击劫持**`frame-ancestors 'none'`
156+
3. **数据泄露**:限制连接目标
157+
4. **内容注入**:限制资源来源
158+
159+
### ⚠️ 潜在风险
160+
161+
1. **`'unsafe-inline'`**
162+
- 允许内联脚本和样式
163+
- Next.js 和 Ant Design 需要
164+
- 风险:可能被 XSS 利用
165+
166+
2. **`'unsafe-eval'`**
167+
- 允许 eval() 函数
168+
- 某些库需要
169+
- 风险:可能执行恶意代码
170+
171+
3. **`img-src https:`**
172+
- 允许所有 HTTPS 图片
173+
- **必需**:用户可以自定义任意图标 URL
174+
- 权衡:功能性 > 严格性
175+
- 风险:可能加载恶意图片(但只是图片,不会执行代码)
176+
177+
### 🔒 如何提高安全性
178+
179+
**方法 1:使用 Nonce**
180+
```html
181+
<!-- 为每个内联脚本生成唯一的 nonce -->
182+
<script nonce="random-value">...</script>
183+
```
184+
185+
**方法 2:使用 Hash**
186+
```
187+
script-src 'self' 'sha256-hash-of-script';
188+
```
189+
190+
**方法 3:严格限制图片来源**
191+
```diff
192+
- img-src 'self' data: https:;
193+
+ img-src 'self' data: https://favicon.im https://your-cdn.com;
194+
```
195+
196+
**注意**:对于导航网站,**不推荐**严格限制图片来源,因为:
197+
- 用户需要自定义任意网站的图标
198+
- 无法预知所有可能的图标域名
199+
- `https:` 通配符是最佳选择
200+
201+
## 测试 CSP 配置
202+
203+
### 1. 浏览器开发者工具
204+
205+
```
206+
F12 → Console
207+
查看 CSP 违规报告
208+
```
209+
210+
### 2. CSP 报告模式
211+
212+
```
213+
Content-Security-Policy-Report-Only: ...
214+
```
215+
- 不阻止资源,只报告违规
216+
- 用于测试新配置
217+
218+
### 3. 在线工具
219+
220+
- [CSP Evaluator](https://csp-evaluator.withgoogle.com/)
221+
- [CSP Validator](https://cspvalidator.org/)
222+
223+
## 监控 CSP 违规
224+
225+
### 添加报告端点
226+
227+
```diff
228+
Content-Security-Policy:
229+
default-src 'self';
230+
...
231+
+ report-uri https://your-domain.com/csp-report;
232+
+ report-to csp-endpoint;
233+
```
234+
235+
### 处理报告
236+
237+
```javascript
238+
// 服务器端接收 CSP 报告
239+
app.post('/csp-report', (req, res) => {
240+
console.log('CSP Violation:', req.body);
241+
// 记录到日志系统
242+
res.status(204).end();
243+
});
244+
```
245+
246+
## 常见问题
247+
248+
### Q: 为什么需要 `'unsafe-inline'`
249+
250+
**A**: Next.js 和 Ant Design 使用内联样式和脚本。移除会导致样式失效。
251+
252+
**更好的方案**
253+
- 使用 Nonce 或 Hash
254+
- 配置 Next.js 使用外部样式
255+
256+
### Q: `img-src https:` 是否太宽松?
257+
258+
**A**: 是的,但有原因:
259+
- 用户可以添加任意网站的链接
260+
- 这些网站的图标来自不同域名
261+
- 无法预知所有可能的域名
262+
263+
**权衡**
264+
- 安全性 vs 功能性
265+
- 当前配置优先功能性
266+
267+
### Q: 如何知道哪些资源被阻止?
268+
269+
**A**: 查看浏览器控制台:
270+
```
271+
Refused to load ... because it violates the following
272+
Content Security Policy directive: ...
273+
```
274+
275+
### Q: 可以完全禁用 CSP 吗?
276+
277+
**A**: 可以,但不推荐:
278+
```diff
279+
- Content-Security-Policy: ...
280+
```
281+
282+
**风险**
283+
- 失去 XSS 保护
284+
- 失去点击劫持保护
285+
- 降低整体安全性
286+
287+
## 推荐配置
288+
289+
### 开发环境(宽松)
290+
291+
```
292+
Content-Security-Policy:
293+
default-src 'self';
294+
script-src 'self' 'unsafe-inline' 'unsafe-eval';
295+
style-src 'self' 'unsafe-inline';
296+
img-src 'self' data: https:;
297+
font-src 'self' data:;
298+
connect-src 'self' https:;
299+
```
300+
301+
### 生产环境(当前配置)
302+
303+
```
304+
Content-Security-Policy:
305+
default-src 'self';
306+
script-src 'self' 'unsafe-inline' 'unsafe-eval' https://static.cloudflareinsights.com;
307+
style-src 'self' 'unsafe-inline';
308+
img-src 'self' data: https:;
309+
font-src 'self' data:;
310+
connect-src 'self' https://cloudflareinsights.com https://favicon.im;
311+
frame-ancestors 'none';
312+
```
313+
314+
### 高安全环境(严格)
315+
316+
```
317+
Content-Security-Policy:
318+
default-src 'self';
319+
script-src 'self' 'nonce-{random}';
320+
style-src 'self' 'nonce-{random}';
321+
img-src 'self' data: https://favicon.im;
322+
font-src 'self';
323+
connect-src 'self' https://favicon.im;
324+
frame-ancestors 'none';
325+
report-uri /csp-report;
326+
```
327+
328+
## 总结
329+
330+
### 当前配置的优点
331+
332+
✅ 保护基本的 XSS 攻击
333+
✅ 防止点击劫持
334+
✅ 允许必要的第三方服务
335+
✅ 支持用户自定义图标
336+
✅ 平衡安全性和功能性
337+
338+
### 当前配置的缺点
339+
340+
⚠️ `'unsafe-inline'``'unsafe-eval'` 降低安全性
341+
⚠️ `img-src https:` 过于宽松
342+
⚠️ 没有 CSP 报告机制
343+
344+
### 建议
345+
346+
1. **短期**:保持当前配置,监控控制台错误
347+
2. **中期**:添加 CSP 报告端点,收集违规数据
348+
3. **长期**:迁移到 Nonce/Hash,移除 `'unsafe-*'`
349+
350+
---
351+
352+
**记住**:CSP 是安全的一层防护,不是唯一防护。还需要:
353+
- 输入验证
354+
- 输出转义
355+
- HTTPS
356+
- 安全的依赖管理
357+
- 定期安全审计

public/_headers

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
Permissions-Policy: camera=(), microphone=(), geolocation=(), interest-cohort=()
1212

1313
# 内容安全策略(CSP)
14-
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://static.cloudflareinsights.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self' https://cloudflareinsights.com; frame-ancestors 'none';
14+
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://static.cloudflareinsights.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self' https://cloudflareinsights.com https://favicon.im; frame-ancestors 'none';
1515

1616
# 性能优化
1717
X-DNS-Prefetch-Control: on

public/sw.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Service Worker for PWA
2-
const CACHE_NAME = 'weiz-nav-v1';
3-
const RUNTIME_CACHE = 'weiz-nav-runtime';
4-
const IMAGE_CACHE = 'weiz-nav-images';
2+
const CACHE_NAME = 'weiz-nav-v2';
3+
const RUNTIME_CACHE = 'weiz-nav-runtime-v2';
4+
const IMAGE_CACHE = 'weiz-nav-images-v2';
55

66
// 需要预缓存的静态资源
77
const PRECACHE_URLS = [

0 commit comments

Comments
 (0)