CSP 配置指南:SpringBoot/Express 实操 + 多域名适配,防 XSS 攻击超简单
🛡️ 普通网站必看!CSP 配置指南:SpringBoot/Express 实操 + 多域名适配,防 XSS 攻击超简单
做普通网站开发时,你是不是总担心这些问题?—— 用户遭遇 XSS 攻击导致信息泄露,页面被注入恶意广告脚本,或者非法 CDN 拖慢加载速度?其实一套合理的CSP(内容安全策略) 就能解决!今天从原理到实战,手把手教你给普通网站配 CSP,还附 SpringBoot、Node.js Express 的完整代码,多域名 + 通配符场景直接抄作业~
一、先搞懂:CSP 到底是啥?普通网站为啥需要它?
CSP(Content Security Policy)简单说就是给浏览器画 “资源白名单” —— 通过规则指定哪些域名的脚本、图片、样式能加载,哪些不能,本质是限制 “不可信资源” 的执行。
📝 Powered by Moshow 郑锴 | 更多技术干货:https://zhengkai.blog.csdn.net/
对普通网站来说,它的作用太关键了:
- 🚫 防御 XSS 攻击:阻断攻击者注入的恶意 JS 脚本,保护用户账号、表单数据等信息
- 🚩 拦截恶意资源:禁止加载非法广告、钓鱼链接等资源,避免网站被 “污染”
- ⚡ 优化资源加载:只允许可信 CDN 和自有域名加载资源,减少冗余请求,提升页面速度
二、CSP 核心指令:普通网站常用这 6 个,记熟够用
CSP 用 “指令 + 值” 定义规则,普通网站开发高频用到的就这几个,关键信息已加粗:
指令 | 作用 | 普通网站常用值示例 |
---|---|---|
default-src | 所有资源的 “默认规则”(兜底) | 'self' (自身域名)、通用 HTTPS 域名 |
script-src | 限制 JS 脚本来源(防 XSS 核心) | 自身域名、可信 CDN(如 jsdelivr)、子域名 |
img-src | 限制图片来源 | 自身域名、图片 CDN、data: (base64 图) |
style-src | 限制 CSS 样式来源 | 自身域名、样式 CDN(如 bootcdn) |
font-src | 限制字体文件来源 | 自身域名、字体 CDN |
frame-ancestors | 限制页面被 iframe 嵌套(防点击劫持) | 'self' 、可信合作域名(如公司其他网站) |
三、框架实操:SpringBoot/Express 配置 CSP(多域名 + 通配符)
这部分是重点!直接给可运行代码,只需替换成你的网站域名和常用 CDN,支持多域名(逗号分隔)和通配符(*.xxx.com
),适配普通网站的资源加载场景。
🔧 场景前提
假设你的网站需要加载这些资源:
- 自有域名:
your-site.com
(主域名)、*.your-site.com
(子域名,如blog.your-site.com、cdn.your-site.com) - 可信 CDN:
https://cdn.jsdelivr.net
(通用 JS/CSS)、https://cdn.bootcdn.net
(前端库)、https://img.abc.com
(图片专用 CDN)
1. SpringBoot 版本配置(2 种通用方式)
SpringBoot 推荐用拦截器(简单通用)或Spring Security(适合有权限控制的网站,如后台管理系统),两种方式都能实现 CSP。
方式 1:拦截器配置(无权限依赖,直接用)
// 1. 新建CSP拦截器:定义规则并添加响应头
@Component
public class CspInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {// 🌟 CSP核心规则:多域名+通配符适配普通网站String cspPolicy = "default-src 'self' https://cdn.jsdelivr.net; " +"script-src 'self' https://cdn.jsdelivr.net https://cdn.bootcdn.net *.your-site.com; " + // 允许3个可信来源的JS"img-src 'self' https://img.abc.com data: *.your-site.com; " + // 支持base64图片和图片CDN"style-src 'self' https://cdn.jsdelivr.net; " + // 样式仅允许自身和通用CDN"font-src 'self' https://cdn.jsdelivr.net; " + // 字体来源限制"frame-ancestors 'self' https://partner.your-site.com;"; // 允许合作网站嵌套自身页面// 强制模式:违反规则的资源会被阻断(上线用)response.setHeader("Content-Security-Policy", cspPolicy);// 测试模式:只上报违规日志,不阻断资源(调试用,需加report-uri)// response.setHeader("Content-Security-Policy-Report-Only", cspPolicy + " report-uri /api/csp/log;");return true;}
}// 2. 注册拦截器:指定生效的网站路径
@Configuration
public class WebConfig implements WebMvcConfigurer {@Autowiredprivate CspInterceptor cspInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 对网站所有页面生效(可根据需求调整路径,如只给前台加:/front/**)registry.addInterceptor(cspInterceptor).addPathPatterns("/**").excludePathPatterns("/api/csp/log"); // 排除CSP日志接口,避免拦截自身}
}
方式 2:Spring Security 配置(适合带登录的网站)
如果网站用了 Spring Security 做登录验证,可直接集成 CSP,不用额外写拦截器:
@Configuration
@EnableWebSecurity
public class SecurityConfig {@Beanpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests(auth -> auth.anyRequest().authenticated() // 示例:所有请求需登录(根据你的网站权限调整)).headers(headers -> headers// 启用CSP并配置规则.contentSecurityPolicy(csp -> csp.policyDirectives("default-src 'self' https://cdn.jsdelivr.net; " +"script-src 'self' https://cdn.jsdelivr.net *.your-site.com; " +"img-src 'self' https://img.abc.com data:; " +"frame-ancestors 'self';")).frameOptions(frame -> frame.sameOrigin()) // 配合防点击劫持);return http.build();}
}
2. Node.js Express 版本配置(用 helmet 中间件,超简单)
Express 推荐用helmet
中间件(专门处理安全相关的响应头,包括 CSP),先装依赖,再写配置,3 步搞定。
步骤 1:安装 helmet 依赖
# npm或yarn都可以
npm install helmet --save
# 或
yarn add helmet
步骤 2:在入口文件(如 app.js)配置 CSP
const express = require('express');
const helmet = require('helmet');
const app = express();// 🌟 配置CSP:多域名+通配符适配普通网站
app.use(helmet.contentSecurityPolicy({directives: {defaultSrc: ["'self'", "https://cdn.jsdelivr.net"], // 默认只允许自身和通用CDNscriptSrc: ["'self'", "https://cdn.jsdelivr.net", "https://cdn.bootcdn.net", "*.your-site.com"], // JS允许3个可信来源imgSrc: ["'self'", "https://img.abc.com", "data:", "*.your-site.com"], // 图片支持CDN、base64、子域名styleSrc: ["'self'", "https://cdn.jsdelivr.net"], // 样式来源限制fontSrc: ["'self'", "https://cdn.jsdelivr.net"], // 字体仅允许可信来源frameAncestors: ["'self'"], // 禁止陌生网站嵌套自身页面(防点击劫持)// 测试阶段加:上报违规日志到接口(上线可删除或保留)// reportUri: "/api/csp/log"}
}));// 普通网站路由示例(如首页、文章页)
app.get('/', (req, res) => {res.send('普通网站首页(CSP已生效)');
});
app.get('/article/:id', (req, res) => {res.send(`文章${req.params.id}(CSP已生效)`);
});// 启动服务
app.listen(3000, () => {console.log('Express服务启动:http://localhost:3000,CSP配置已生效');
});
四、普通网站避坑指南:这 4 个错误千万别犯!
- ❌ 通配符 “乱用” 导致不安全
*.your-site.com
只匹配子域名(如blog.your-site.com),不匹配主域名(your-site.com),需单独加主域名:your-site.com *.your-site.com
script-src
别用*
(会允许任何网站的 JS,等于没开 CSP),必须指定具体域名;frame-ancestors
不支持*
,只能写明确域名。
-
✅ 别漏了
data:
/blob:
资源如果网站有用 base64 图片(如<img src="data:image/png;base64,...">
)或 Blob 视频,必须在img-src
/media-src
里加data:
/blob:
,否则资源会被阻断,页面出现 “裂图”“无法播放”。 -
❌ 保留
unsafe-inline
导致 XSS 防护失效unsafe-inline
允许加载内联脚本(如<script>alert(1)</script>
)或内联样式(如<div style="...">
),会直接抵消 CSP 的 XSS 防御效果。除非是老项目兼容(实在没办法),否则坚决删除unsafe-inline
。 -
✅ 测试阶段用 “Report-Only” 模式上线前先把
Content-Security-Policy
换成Content-Security-Policy-Report-Only
,这样违规资源不会被阻断,但会把问题上报到reportUri
接口,能快速定位漏加的域名,避免影响用户。
五、测试 CSP 是否生效:2 个简单方法(普通网站通用)
- 浏览器控制台直接查打开网站页面,按 F12 进入 “控制台”(Console):
- 如果 CSP 配置成功:没有 “Refused to load the script/style/image...” 的红色错误
- 如果配置有问题:会提示 “被 CSP 阻止”,并显示 “违反的指令”(如
script-src
漏加域名),按提示补充即可。
- 用 “日志接口” 收集问题在测试模式下配置
reportUri: "/api/csp/log"
,然后写一个简单的接口接收日志(如 SpringBoot 的@PostMapping("/api/csp/log")
),能收集所有用户端的 CSP 违规情况,适合多页面的大型网站。
六、总结:普通网站 CSP 配置模板(直接抄)
不管是 SpringBoot 还是 Express,普通网站的 CSP 核心规则都可以参考这个模板,只需替换your-site.com
为你的实际域名、https://img.abc.com
为你的图片 CDN:
default-src 'self' https://cdn.jsdelivr.net;
script-src 'self' https://cdn.jsdelivr.net https://cdn.bootcdn.net your-site.com *.your-site.com;
img-src 'self' https://img.abc.com data: your-site.com *.your-site.com;
style-src 'self' https://cdn.jsdelivr.net;
font-src 'self' https://cdn.jsdelivr.net;
frame-ancestors 'self';
最后想问大家:你在普通网站开发中,遇到过 XSS 攻击或恶意资源注入吗?当时是怎么解决的?评论区聊聊~
📝 Powered by Moshow 郑锴 | 更多技术干货:https://zhengkai.blog.csdn.net/