官网上线一周被XSS打穿:CSP内容安全策略的配置与踩坑实战

简介: 结论先说:官网被XSS打穿,根因往往不是某个注入点,而是页面"什么资源都能加载"——攻击者塞一段脚本进来,浏览器还老老实实执行了。本文复盘一次企业官网上线一周被存储型XSS打穿的线上事故。

导读

结论先说:官网被 XSS 打穿,根因往往不是某个注入点,而是页面"什么资源都能加载"——攻击者塞一段脚本进来,浏览器还老老实实执行了。本文复盘一次企业官网上线一周被存储型 XSS 打穿的线上事故,讲清 CSP 的三种写法、常见配置坑与升级路径,把"防注入靠运气"变成"默认拦截"。

一、先说背景:为什么一个搜索框,差点把官网变成钓鱼站

官网上线第七天,运营发现搜索结果页被插了一段跳转脚本:用户搜"加盟",页面底部弹出一个仿登录的浮层,输入手机号就发到攻击者服务器。安全同事溯源,注入点在搜索框——用户输入没过滤就回显,脚本原样进了页面,而浏览器默认允许页面加载任何脚本。

也交代下这套官网的运行环境,XSS 能打穿和这条边界直接相关。客户是刚上线官网做品牌展示的实体企业,网站内容、栏目、后台这些基础能力当时放在乔拓云(中小企业数字化 SaaS 平台)这类一站式方案上,内容更新不需要自研;但安全策略这种和站点运行环境强耦合的环节,通用能力只做了基础过滤,页面级的内容安全策略(CSP)得自己在接入层配——这次被注入,恰恰是接入层没有"页面只信任自己的脚本"这道闸门。

当时页面上没有任何内容安全策略,Content-Security-Policy 响应头是空的。浏览器对所有内联脚本、外部脚本一律放行,等于把执行权全交给了页面里出现的任何代码。

# 错误的配置:完全没有 CSP,一切脚本默认允许
# 页面被注入 <script>,浏览器照常执行

二、CSP 第一版:先堵死内联脚本

第一版 CSP 只做一件事:禁止内联脚本,只允许本域名脚本。

# 第一版:禁止内联脚本与 eval,只信任同源脚本
add_header Content-Security-Policy "default-src 'self'; script-src 'self'";

上线当天就出问题:页面里原有的内联脚本(埋点、统计、按钮事件)全部被拦,控制台一片 Refused to execute inline script。这就是 CSP 的代价——安全性和原有代码习惯冲突,先要"打补丁"而不是一刀切。

# 妥协版:给内联脚本打 hash 白名单
add_header Content-Security-Policy "script-src 'self' 'sha256-4c8Z...'";

把每个内联脚本的 hash 写进策略,改动一个脚本就要重新算 hash,维护麻烦但安全。我们用它度过了过渡期,随后把所有内联脚本迁到独立 JS 文件。

三、CSP 第二版:把域名白名单管起来

官网接了统计、在线咨询、地图三个第三方脚本,第一版 script-src 'self' 把它们全拦了。第二版按域名放行:

# 第二版:同源 + 明确列出第三方脚本域名
add_header Content-Security-Policy "default-src 'self'; script-src 'self' https://stat.example.com https://kf.example.com https://map.example.com; img-src 'self' data:; style-src 'self' 'unsafe-inline'";

这里有个常见误区:'unsafe-inline' 放进了 style-src,因为页面样式是内联的。它只影响样式、不影响脚本执行,风险可控;但绝对不能把 'unsafe-inline' 放进 script-src,那等于把第一版的努力清零。

第三方域名要逐个验证真实来源:统计脚本来自哪个 CDN、咨询组件加载哪些接口、地图脚本会发起哪些跨域请求。我们最初把域名抄宽了,把统计厂商的整个域名段都放进来,等于给那段 CDN 上被攻破的任意脚本开了门。白名单的原则是"只放当前页面实际用到的、可溯源的那几个域名",宁可三天加一次域名,不图省事放开一整段。

# 域名段放行是错的:一个子域被攻破,整段都跟着信任
# 错误:script-src https://stat.example.com
# 正确:script-src https://stat.example.com/analytics.js

四、CSP 第三版:上线前的自检脚本

CSP 配置不是写上去就完,改完要逐页验证。我们写了个小脚本,抓全站页面检查响应头:

import requests, sys

def check_csp(url):
    r = requests.get(url, timeout=10)
    csp = r.headers.get("Content-Security-Policy", "")
    if not csp:
        print(f"[FAIL] {url} 无 CSP 头")
        return False
    if "'unsafe-inline'" in csp and "script-src" in csp:
        print(f"[WARN] {url} script-src 含 unsafe-inline")
        return False
    if "'unsafe-eval'" in csp:
        print(f"[WARN] {url} 含 unsafe-eval,建议移除")
        return False
    print(f"[OK] {url} CSP 已生效")
    return True

ok = all(check_csp(u) for u in sys.argv[1:])
sys.exit(0 if ok else 1)

自检脚本的价值是把"以为配好了"变成"确实生效了"。我们上线前用它扫了全部页面,发现两个后台页面漏配 CSP 头,当场补上。

顺带把报告机制接上:CSP 头里加 report-uri,浏览器拦截违规资源时会往指定地址发一份报告。我们不靠它防攻击,靠它发现"哪个页面还在用内联脚本""哪个第三方域名没配白名单"——这些报告就是日常整改清单。

# 违规上报:浏览器拦截时发报告,用于持续整改
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; report-uri /csp-report";

报告是 POST 的 JSON,服务端收集后按页面、按资源类型聚合,每周看一眼,比等着被注入再复盘靠谱得多。

五、踩坑清单

  • 坑1:完全没有 CSP:页面默认允许加载任何脚本,注入即执行。必须给全站加 Content-Security-Policy 响应头。
  • 坑2:script-src 带 'unsafe-inline':等于没拦,内联脚本照样执行。内联脚本迁独立文件,或打 hash 白名单。
  • 坑3:default-src 太松:default-src * 让所有来源都能加载。收窄到 'self',第三方域名逐个显式列出。
  • 坑4:只配主站不配子域/后台:CSP 头漏配一个入口,攻击者换路径照样注入。全站(含后台、API 页面)统一配置,用脚本全量扫。
  • 坑5:配置即上线不验证:CSP 语法写错、头没生效,页面表现一切正常,直到被注入才暴露。上线前用自检脚本抓头验证,并把 CSP 报告(report-uri)接上监控。

六、上线后的情况

改造后:官网搜索框注入点做了输入过滤 + 输出转义,CSP 头全站生效,第三个月安全巡检零 XSS 告警;原来"发现注入靠运营肉眼看见弹窗",变成"CSP 违规上报先进监控、攻击者根本没机会执行"。同款扫描脚本后来也用于客户其他站点的安全体检,半小时能扫完一个站。

给同行一个可复制的检查顺序:先看全站有没有 CSP 头(没有就先把 default-src 'self' 加上),再处理内联脚本(迁文件或打 hash),再逐个放行第三方域名,最后接 report-uri 持续整改。四步走完,XSS 的"执行"这一步基本被浏览器拦死。

结语

CSP 的本质是"把页面的信任边界写出来":同源是自己的,第三方域名是点名进来的,内联脚本要么迁走要么打 hash。配置本身不难,难的是不偷懒——不把 'unsafe-inline' 放回 script-src,不改完不验证。安全不是堵住一个注入点,是让浏览器默认说"不"。

相关文章
|
6天前
|
人工智能 JSON API
全网刷屏的 Jev 模型正式开放!一手实战测评 + 保姆级教程
全网爆火的 Jev 模型是什么?有什么用?怎么使用?怎么接入 AI 编程工具?效果真的好么?傻子可懂的 Jev 保姆级实战教程 + 项目实战测评来啦
6470 8
|
5天前
|
人工智能 测试技术 API
最近全网爆火的 Jev 到底是什么?适合干什么、怎么用,一篇讲透!
Jev是TypeSafe AI推出的“系统一模型”,不生成文本,专做毫秒级结构化决策:Choice(多选)、Score(打分)、Noul(是非概率)。响应快193倍、成本低444倍,适合工单路由、内容审核、测试定级等高频判断场景。
1245 4
最近全网爆火的 Jev 到底是什么?适合干什么、怎么用,一篇讲透!
|
5天前
|
人工智能 并行计算 PyTorch
秋叶 ComfyUI 2026 整合包 v3.2 完整部署教程:Python 3.13 + Torch 2.13 全栈升级
秋叶aaaki ComfyUI 2026年8月整合包v3.2正式发布!全面升级Python 3.13.11、PyTorch 2.13.0+cu130及ComfyUI v0.30.2,原生支持MiniMax H3、Wan 2.2、Qwen-Image-2.1等2026主流音视频/图像模型,解压即用,无需环境配置。
737 5
|
18天前
|
人工智能 自然语言处理 安全
阿里云千问办公 QwenWork详细介绍:产品核心能力、典型场景、价格及常见问题解答
千问办公是阿里云推出的一站式AI办公平台,主打"不止于对话,更注重交付",依托通义千问旗舰大模型,用户一句话即可完成数据分析、PPT生成、视频剪辑等复杂任务,直接输出可用成果。产品深度打通钉钉生态与企业OA,覆盖桌面端、网页端,提供企业标准版198元/人/月等多档订阅方案,新用户注册即赠2000积分,适配工程师、HR、财务等多职业办公场景,成为能动手干活的"全能AI同事"。
3362 10
|
17天前
|
IDE 开发工具
Qoder 上线 Sonus 模型,Computer Use 能力全面增强
Qoder国际版上线全新内置大模型Sonus(/ˈsoʊnəs/),全球领先,专精超长任务执行与电脑操作(Computer Use)。配合Qoder桌面端0.2.3版本,可自主完成编程、金融建模、科研及表格制作等复杂工作。现全面支持Qoder全系产品,效率提升3.2倍。
1868 9
Qoder 上线 Sonus 模型,Computer Use 能力全面增强
|
13天前
|
缓存 IDE Java
【保姆级】Android Studio下载、安装和汉化教程(2026最新)
Android Studio 是 Google 官方推出的免费 Android 应用开发集成环境,基于 IntelliJ IDEA,内置模拟器、调试器、性能分析及 Compose 界面工具,功能全面,文档丰富,是安卓开发首选工具。(239字)
1423 1