从 XSS 到纵深防御

Web 安全攻防里,XSS 跨站脚本是头号问题:攻击者把恶意脚本注入页面,偷 Cookie、劫持会话、篡改页面。单纯靠转义输出做防护远远不够,因为漏洞可能藏在第三方库、富文本内容或历史代码里。现代 Web 安全的思路是纵深防御:输出编码、输入校验是第一条线,CSP 内容安全策略是第二条线,让即使注入成功也跑不起来。

CSP 通过响应头声明"页面允许加载哪些来源的资源、允许执行哪些脚本",浏览器据此拦截违规资源。它把"信任一切"的默认策略改成"白名单放行",是 XSS 的最后一道闸门。

本文核心知识点
  • CSP声明资源白名单,拦截注入脚本。
  • CORS跨域访问的授权机制。
  • SRI校验外链资源哈希防篡改。
  • 响应头HSTS、X-Frame-Options 等加固。
Web 安全 CSP 配图:蓝紫渐变背景左侧纯色圆角屏幕方块,中间横竖交叉网格屏障,右侧三台纯色服务器方块,彩色直线穿过屏障连接两端,扁平科技插画
请求穿过网格屏障才抵达服务器,正是内容安全策略过滤放行的意象。

CSP 内容安全策略

CSP 的核心是一组指令:default-src 兜底所有资源类型,script-src 管脚本来源,style-src 管样式,img-src 管图片,connect-src 管 fetch 与 XHR,frame-ancestors 管谁可以嵌本页。指令值可以是来源白名单、关键字 none 或 self,或者 unsafe-inline 这类放行标记。

# 一个严格但可用的 CSP
Content-Security-Policy:
  default-src 'self';
  script-src 'self' https://cdn.example.com;
  style-src 'self' 'unsafe-inline';
  img-src 'self' data:;
  connect-src 'self' https://api.example.com;
  frame-ancestors 'none'

配置 CSP 最容易踩的坑是 unsafe-inline:一旦允许内联脚本,XSS 注入的脚本就能直接执行,CSP 形同虚设。现代站点应该用 nonce 或 hash 放行必要的内联脚本,而不是一刀切 unsafe-inline。先开 Content-Security-Policy-Report-Only 观察违规报告,再切强制模式。

CORS 与同源策略

同源策略是浏览器的安全基石:默认情况下,页面只能读取同源资源,跨源读取被禁止。CORS 是服务器主动放行的机制:服务器在响应头里声明 Access-Control-Allow-Origin,浏览器才允许前端读取跨源响应。

# 服务端 CORS 响应头示例
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Credentials: true

配置 CORS 的错误示范是 Access-Control-Allow-Origin 直接写星号还带上凭据,等于向全网开放读取。正确做法是白名单精确到域名,带 Cookie 的请求必须回显具体来源而不能用星号。还要小心预检请求 OPTIONS 的缓存与放行,预检失败是跨域联调最常见的问题之一。

安全响应头

除 CSP 外,还有一组响应头值得统一加固。X-Frame-Options 或 CSP 的 frame-ancestors 防止点击劫持;X-Content-Type-Options: nosniff 禁止浏览器嗅探 MIME 类型;Referrer-Policy 控制外链带出的来源信息;Permissions-Policy 限制摄像头、定位等敏感能力;HSTS 强制 HTTPS 连接。

X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=()
Strict-Transport-Security: max-age=31536000; includeSubDomains

这些头大多一条配置就能生效,成本极低,收益却是实打实的:很多自动化攻击工具就是冲着默认配置来的,加固后的响应头能直接挡掉一批低水平攻击。建议用一个安全头检查工具定期扫描全站。

子资源完整性与 SRI

CDN 上的公共脚本一旦被攻破或劫持,全站页面都会中毒。SRI 子资源完整性让浏览器在加载脚本前校验文件哈希:integrity 属性带上文件的 SHA-384 摘要,浏览器计算比对,不匹配就拒绝执行。

<script src="https://cdn.example.com/lib.js"
        integrity="sha384-BASE64HASH=="
        crossorigin="anonymous"></script>

使用 SRI 的注意点:CDN 换版本或做 minify 后哈希会变,发布流程要同步更新 integrity;带 integrity 的跨域脚本必须加 crossorigin 属性;本地同源脚本也可以加 SRI 防篡改。公共依赖尽量固定版本加 SRI,比依赖 CDN 的信誉更可靠。

表单与第三方脚本风险

表单是攻击面的重灾区:SQL 注入、命令注入多从输入来;CSRF 利用用户已登录的会话在别的站点发起伪造请求。表单防护的基本盘是后端参数校验加预编译语句,CSRF 用双重 Cookie 或 SameSite 属性:SameSite=Lax 默认就能挡掉大部分跨站请求伪造。

第三方脚本是另一个隐蔽风险:统计脚本、广告脚本、客服组件都拿到全站权限,任何一个被攻破都是供应链攻击。管理第三方脚本的原则:尽量用子域隔离与 CSP 限定其权限;非必要不引入;必须引入时加 SRI 并单独放在受限的 script-src 来源下,禁止第三方脚本访问敏感数据。

# 把第三方脚本限制在独立来源
script-src 'self' https://analytics.example.com
                  https://support.example.com;