提示

返回博客列表

直链别裸奔:用 Python 给下载地址加时效签名 token

直链别裸奔:用 Python 给下载地址加时效签名 token

前面讲防盗链靠 Referer,但 Referer 能被伪造,真要防泄露得靠签名 URL——给直链加一个带过期时间的 token,服务端校验通过才放行。这篇用一个最小可用的 Python 实现讲清楚,以及几个别踩的坑。

思路

  1. 服务端有个只有自己知道的 SECRET
  2. 生成下载链接时,把 过期时间 exp路径 path 拼上,用 HMAC 算个签名 sig
  3. expsig 附到 URL 参数;
  4. CDN/后端收到请求,用同样的 SECRET 重算签名比对,且检查 exp 没过期,才返回文件,否则 403。

生成签名(Python)

import hmac, hashlib, time, base64, urllib.parse

SECRET = b'your-secret-key'
def sign(path: str, ttl=600):
    exp = int(time.time()) + ttl
    raw = f"{path}:{exp}".encode()
    sig = hmac.new(SECRET, raw, hashlib.sha256).hexdigest()
    return exp, sig

exp, sig = sign("/videos/a.mp4")
url = f"https://cdn.example.com/videos/a.mp4?exp={exp}&sig={sig}"
print(url)

ttl=600 表示 10 分钟有效。

校验(服务端/中间件)

def verify(path: str, exp: str, sig: str):
    if int(exp) < time.time():
        return False  # 过期
    raw = f"{path}:{exp}".encode()
    expect = hmac.new(SECRET, raw, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expect, sig)

一定用 hmac.compare_digest 比较,别用 ==,后者有时序攻击风险。

坑一:path 必须规范化和双方一致

你签名用 /videos/a.mp4,CDN 转发后可能变成 //videos//a.mp4 或带 query 的,签名就对不上。校验端先把 path 用 urllib.parse 规范化(unquote、去多余斜杠)再算。

坑二:CDN 会改写 URL

Nginx/CDN 可能把 ?exp=&sig= 透传也可能吞掉。配置上要确认参数被原样传给后端校验逻辑。我踩过 CDN 把 sig 里的 +/= 转义的坑,校验端 unquote 一下。

坑三:SECRET 泄露等于没签

签名的安全性全在 SECRET。别硬编码在前端、别进 git、用环境变量或 KMS。前端只能拿到"已签好的 URL",绝不能拿到 SECRET。

和 Referer 的关系

签名解决"链接本身能不能用",Referer 解决"从哪来的"。两者叠加:签名防泄露、Referer 防盗链。前面那篇防盗链是客户端视角,这篇是服务端视角,配合着用。

想亲手试试?用 VidDown 一键解析下载

粘贴视频链接即可解析,多平台支持、网页端即用;下载桌面客户端解锁海外平台本地解析,开通会员更享不限次下载。

顶部