浏览器 Web 安全基础系列

  1. 浏览器怎么判断是不是同一个网站

  2. Cookie 为什么会自动携带

  3. Session 登录状态为什么可能被劫持

  4. HTTPS 为什么仍然不够

  5. CORS 到底在限制什么

  6. CSRF 是怎么冒用登录状态的

  7. CSRF 如何防御

  8. XSS 为什么能在你的网站执行

  9. CSP 与浏览器安全响应头

  10. Clickjacking 点击劫持

  11. 第三方资源为什么危险

  12. 防盗链到底防什么(当前篇)

盗链指其他网站直接引用你的图片、视频或下载地址,内容显示在对方页面,带宽却由你的服务器或 CDN 承担。最基础的防盗链检查 Referer;私有资源则需要真正的权限校验或短期签名 URL。

Referer 防盗链流程

Referer 防盗链

其他网站引用:

<img src="https://cdn.example.com/images/photo.webp">

浏览器向你的 CDN 请求时可能发送:

Referer: https://other.example/article

服务器允许自己站点,拒绝其他来源:

const allowedHosts = new Set(['www.example.com', 'blog.example.com']);

function allowedReferer(req) {
  const referer = req.headers.referer;
  if (!referer) return false;

  try {
    return allowedHosts.has(new URL(referer).hostname);
  } catch {
    return false;
  }
}

Nginx 示例:

location ~* \.(jpg|jpeg|png|webp|gif)$ {
    valid_referers server_names *.example.com;
    if ($invalid_referer) {
        return 403;
    }
}

空 Referer 怎么处理

用户直接在地址栏打开资源、隐私策略、浏览器插件或协议降级都可能导致 Referer 缺失。

  • 允许空 Referer:兼容性更好,但更容易绕过;

  • 拒绝空 Referer:更严格,但可能误伤下载、客户端和分享场景。

普通博客图片通常可以允许空 Referer并拒绝明确的外站来源;高价值资源不要只靠 Referer。

Referer 可以伪造

浏览器页面脚本不能随便设置 Referer,但 curl、下载器和攻击者服务器可以:

curl -H "Referer: https://www.example.com/"   https://cdn.example.com/images/photo.webp

所以 Referer 防盗链主要防“网页直接引用”,不是强身份认证,也防不了有意下载。

签名 URL

私有文件可以生成带过期时间和 HMAC 的地址:

/download/report.pdf?expires=1783900000&signature=...

服务端签名:

import crypto from 'node:crypto';

function sign(path, expires, secret) {
  return crypto
    .createHmac('sha256', secret)
    .update(`${path}:${expires}`)
    .digest('base64url');
}

验证时检查过期时间和签名:

function verify(path, expires, supplied, secret) {
  if (Date.now() >= Number(expires) * 1000) return false;

  const expected = sign(path, expires, secret);
  const a = Buffer.from(expected);
  const b = Buffer.from(String(supplied || ''));

  return a.length === b.length && crypto.timingSafeEqual(a, b);
}
签名 URL 流程

签名必须覆盖资源路径和过期时间,否则攻击者可能把有效签名换到另一个文件上。还可以绑定用户 ID、资源 ID、清晰度或下载次数,但绑定 IP 容易误伤移动网络和代理用户。

登录鉴权与签名 URL 的关系

业务服务先检查用户是否有权访问文件,再签发短期 URL:

用户请求下载
→ 业务服务验证登录和资源权限
→ 生成 5 分钟签名 URL
→ CDN/资源服务验证签名
→ 返回文件

签名 URL 不是权限判断本身。服务端不能只要用户知道 fileId 就签发地址,仍要检查资源归属和角色权限。

为什么图片常用 URL 签名而不是 Authorization 头

<img><video> 和下载链接不方便统一添加自定义 Authorization 请求头。Cookie 或签名 URL 更适合浏览器原生资源标签。API 数据则更常使用 Cookie Session 或 Bearer Token。

视频保护

HLS 视频包含播放列表、分片、字幕和可能的密钥:

master.m3u8
segment-001.ts
segment-002.ts
key

只保护主 m3u8 不够,分片地址仍可能被下载。常见组合:

  • CDN Token 鉴权;

  • 短期签名 URL 或签名 Cookie;

  • 分片和密钥鉴权;

  • DRM;

  • 用户水印;

  • 并发、速率和异常下载检测。

任何能在终端播放的内容都无法保证绝对不可复制,目标是提高成本、限制规模和保留追踪能力。

防盗链与 CORS

CORS 控制 JavaScript 是否能读取跨源响应。<img> 即使没有 CORS 许可,也可能正常显示跨源图片。因此 CORS 不能替代防盗链。

Referer 防盗链决定是否返回资源;签名 URL 和登录鉴权决定请求是否获得授权。这三者层级不同。

选型建议

资源

推荐方案

普通博客图片

CDN Referer 白名单,按业务决定是否允许空 Referer

登录后附件

业务权限校验 + 短期签名 URL

软件安装包

短期签名 URL + 下载频率限制

会员视频

CDN Token、分片鉴权、短期 URL

高价值视频

DRM + CDN 鉴权 + 水印 + 风险检测

常见误区

防盗链等于防下载

不对。资源最终到达用户设备,只能增加滥用成本,不能保证不可复制。

URL 足够复杂就安全

隐藏地址不是授权。应使用不可伪造签名和服务端权限校验。

签名 URL 有效期越短越好

太短会导致弱网、断点续传和长视频播放失败,需要按资源大小和播放模式设计续签机制。

小结

Referer 防盗链适合减少普通网页盗用;签名 URL 适合限制链接构造和有效时间;登录鉴权负责判断用户是否有权限。高价值资源通常需要三层以上组合,而不是寻找“绝对防下载”的单一技术。

参考资料