签名 URL 刷新导致审批失效?别急着删掉所有 query

签名 URL 刷新导致审批失效?别急着删掉所有 query

一个真实而隐蔽的失败是:平台草稿连续两次回读,标题、正文和图片对象都相同,内容 SHA 却不一样。原因不是有人改稿,而是图片 CDN 每次返回了新的过期时间和签名参数。

如果批准令牌绑定原始 Markdown SHA,签名刷新会让刚签发的批准立即失效;如果为了省事删除所有 URL 查询参数,又可能把本应不同的资源合并。这个问题的核心不是"哈希算法怎么选",而是安全边界应该忽略什么、继续绑定什么。

冲突:密码学正确,业务语义却错位

SHA-256 对一个字符的变化都敏感,这是优点。可审批系统关心的可能是"图片对象有没有换",而不是"访问这个对象的短期凭证有没有刷新"。

因此需要同时承认两件事:

  • 原始 Markdown 的确变了,不能伪装成字节完全一致。
  • 如果只变了受信任图片 URL 的短期签名,发布对象可能仍是同一个。

把两个判断压成一个 SHA,迟早会让存储完整性和业务身份互相打架。

推荐决策:双哈希,而不是放宽一个哈希

我的推荐是保留两条证据链:

python 复制代码
strict_sha = sha256(raw_markdown)
approval_sha = sha256(canonicalize_signed_images(raw_markdown))

strict_sha 服务于存储、审计和逐字回读;approval_sha 只服务于已经定义过等价关系的批准或幂等边界。批准记录仍绑定文章 ID、revision 和草稿 ID,稳定摘要不能单独成为通行证。

规范化函数也必须是窄能力:只处理 Markdown 图片 URL,只接受 HTTPS,只命中明确的 CDN 域名,并且只删除已经验证会刷新的签名字段。

为什么不能直接 url.split('?')[0]

因为 query 未必都是凭证。尺寸、格式、语言、版本甚至对象选择都可能由 query 决定。下面三种变化必须区分:

  1. signatureexpires 刷新:在白名单契约下可以视为同一对象。
  2. /bucket/a.png 变成 /bucket/b.png:对象变了,必须拒绝旧批准。
  3. ?version=1 变成 ?version=2:如果版本影响内容,也必须拒绝。

安全规范化不是"去掉噪声",而是把经过证据确认的易变凭证与资源身份分离。

最小实现与关键测试

python 复制代码
from urllib.parse import parse_qsl, urlencode, urlsplit, urlunsplit

EPHEMERAL = {"signature", "expires", "policy"}
TRUSTED = {"img.example.com"}

def canonical_url(url: str) -> str:
    parsed = urlsplit(url)
    if parsed.scheme != "https" or parsed.hostname not in TRUSTED:
        return url
    stable_query = [
        (key, value)
        for key, value in parse_qsl(parsed.query, keep_blank_values=True)
        if key not in EPHEMERAL
    ]
    return urlunsplit(
        (parsed.scheme, parsed.netloc, parsed.path, urlencode(stable_query), "")
    )

测试不能只覆盖"签名刷新后相等",还必须证明边界没有被放宽:

  • 域名改变,稳定摘要必须改变。
  • 对象路径改变,稳定摘要必须改变。
  • 正文任意字符改变,稳定摘要必须改变。
  • 保留型 query 改变,稳定摘要必须改变。
  • 规范化执行两次与执行一次结果相同。

失败恢复怎么做

即使摘要稳定,发布结果未知时也不能重放批准。正确顺序仍是:先读草稿或目标对象的当前状态;若已经生成文章 ID,就验证审核页或公开页;只有明确未提交,才基于当前 revision 重新批准。

这能防止两个相反的问题:签名刷新造成假冲突,以及网络超时造成重复提交。

收束

内容指纹不是纯技术细节,而是业务身份的可执行定义。设计它时最该问的不是"用什么哈希",而是"哪些变化代表换了对象,哪些只是访问凭证刷新"。

一个可靠答案通常是双哈希加窄规范化:严格证据不丢,业务批准不被临时签名误伤,任何域名、对象路径和正文变化仍然 fail closed。

发布前门禁

  • 重点呈现方案冲突与取舍
  • 保留严格 SHA 与稳定 SHA 两套证据
  • 对域名、对象路径和正文变化保持拒绝
  • 未把单个平台现象夸大为所有 CDN 的统一规则
相关推荐
牧羊人.3331 小时前
Python 办公自动化从入门到入土|09 数据容器之字典
开发语言·python
weixin_618232301 小时前
OpenAI 因安全暂停 Astra:前沿 Agent 第一次撞上“关键级”红线
安全
Zane19941 小时前
类变量与实例变量:一个共享列表引发的线上事故
后端·python
Python私教1 小时前
一次性批准令牌:挡住旧授权误发新版本的工程设计
python·安全
for_ever_love__2 小时前
python基础语法学习: 数据容器
windows·python·学习
aiqianji2 小时前
文风接近真人的AI生成短篇小说软件有哪些?
人工智能·python
漫路在线2 小时前
无境红日靶场五 wp
网络·安全·web安全
Sylvia33.2 小时前
从轮询到推送:足球数据API架构演进与火星数据技术拆解
java·服务器·网络·python·websocket·架构
瑞码空间3 小时前
Python爬虫进阶实战笔记
开发语言·python·计算机·python爬虫