⭐ 推荐:社区规则条款 V1.0

OSS 签名报 403 的常见原因:Date 头与资源路径的坑

CLARA轻量论坛系统
CLARA轻量论坛系统 星耀SVIP星耀SVIP管理员管理员 黑卡会员黑卡会员
发布于 2026-09-28 20:00 ·99 浏览 ·0 回复
内容摘要

OSS 签名 403 应先看返回错误码区分签名错误、时间偏移或权限问题;SignatureDoesNotMatch 时重点排查 Date 头是否符合 GMT 格式、UTC 时区、时间同步及代理改写,以及 CanonicalizedResou

看完这篇,你能在十分钟内自己判断 OSS 签名 403 到底是 Date 头写错了,还是资源路径对不上,而不是反复换 SDK、反复贴同一段代码问人。

第一步:先看清 403 里的错误码,别急着改代码

403 只是一个 HTTP 状态码,OSS 返回的 XML 里才有真答案。用 curl 打一次请求,看 <Code> 字段:

curl -i "https://your-bucket.oss-cn-hangzhou.aliyuncs.com/test.txt" -H "Authorization: OSS ..."
  • SignatureDoesNotMatch:签名算错了,本文主角,继续往下看。
  • RequestTimeTooSkewed:客户端时间和 OSS 服务器差了超过 15 分钟,直接查时间同步。
  • AccessDenied:签名其实是对的,是权限、IP 白名单、Referer 或防盗链拦的,别浪费时间改签名。
  • InvalidAccessKeyId:AccessKeyId 不存在或被禁用。

注意:不要拿 403 的 status code 当唯一线索。同一个 403 背后有四种完全不同的原因,错误码不同,改的方向完全不同。

第二步:Date 头的四个坑

签名串里的 Date,必须和你 HTTP 请求头里实际发送的 Date 完全一致。常见错误:

1. 格式写错。 必须是 HTTP-date 格式的 GMT 字符串,例如:

Wed, 21 Oct 2015 07:28:00 GMT

不是 2025-01-01 12:00:00,不是 ISO8601 带毫秒,也不是本地时间不带时区后缀。

2. 时区没转 UTC。 服务器在 +08:00,直接 date 出来的是北京时间,直接塞进签名就是错的。生成时用:

date -u '+%a, %d %b %Y %H:%M:%S GMT'

3. 客户端和服务器时间漂移。 虚拟机挂起后恢复、Docker 容器没继承宿主时间、树莓派没装 RTC,都会导致时间慢几分钟到几小时。检查:

date -u
ntpdate -q ntp.aliyun.com

注意:不能只把本地时间「手动调准一次」。容器重启、宿主迁移后还会漂,正确做法是让宿主机跑 NTP/chrony,容器共享宿主时间。

4. 中间层把 Date 头改了。 Nginx 反代、CDN、API 网关默认会自己补一个 Date 响应头,某些代理也会覆写请求头。如果你的服务端在代理后面,先直连 OSS 复现一次,确认是不是代理动的手。

另外,用 V4 签名时会用 x-oss-date 头。如果你同时传了 Date 和 x-oss-date,两者不一致,照样 403。

第三步:资源路径(CanonicalizedResource)的五个坑

签名串最后一段是资源路径,规则是「跟请求实际走的那条路一致」,不是「你觉得它应该是什么」。

1. Bucket 前缀要不要带。 走路径风格 oss-cn-hangzhou.aliyuncs.com/bucket/object 时,签名里要带 /bucket/object;走 bucket 域名或 CNAME 自定义域名时,规则不同(CNAME 场景通常不带 BucketName)。最稳的验证方式是:用官方 SDK 生成一次同样的请求,把它内部拼出的 StringToSign 打印出来,和自己的对比。

2. 是否 URL 编码。 这是最高频的坑:URL 里必须出现 %E4%B8%AD 这类编码,而签名串里用的是原始 ObjectKey 还是编码后的,取决于你用的是 V1 还是 V4 签名,两者规则不同。混着抄示例代码,必错。

3. 空格和加号。 路径里的空格要编码成 %20,写成 + 就变成另一个 key 了。

4. 多余和缺失的斜杠。 ObjectKey 是 a/b.txt,签名里写成 //a/b.txt 或漏掉前导斜杠,都会 SignatureDoesNotMatch。大小写也一样,OSS 的 key 是大小写敏感的。

5. Query 参数没参与签名。 ?acl、?uploads、?response-content-disposition=... 这类子资源和覆盖响应头参数,必须按规则参与签名,少一个就报错。

注意:如果你的请求经过自建 Nginx 转发,检查 location 和 proxy_pass 末尾的斜杠。proxy_pass http://oss/; 和 proxy_pass http://oss; 传给后端的路径不一样,会直接改写资源路径,签名自然对不上。

第四步:把 StringToSign 打出来,一眼定位

阿里云 V1 签名的原始串结构是:

VERB \n
Content-MD5 \n
Content-Type \n
Date \n
CanonicalizedOSSHeaders
CanonicalizedResource

写个十几行的脚本,把这段字符串打印出来,再手动 curl 一次带上相同的头,两边一比就知道差在哪。排查时按这个顺序看:Date 自己算的高频错,然后是资源路径的编码,最后才是 Content-Type 和 Content-MD5 是否和实际请求头一致。

注意:Content-Type 和 Content-MD5 也在签名串里。你签名时写了 text/plain,实际请求头却是 application/octet-stream,一样 403,而且报的还是 SignatureDoesNotMatch。

小结

  • 403 先看 XML 里的 Code,SignatureDoesNotMatch 才去查签名,RequestTimeTooSkewed 直接对时,AccessDenied 查权限。
  • Date 必须 GMT 格式、UTC 时区、和请求头一致,客户端与服务器时间差不能超 15 分钟。
  • 资源路径要和实际请求走的域名、Bucket 前缀、编码方式完全对应,多一个斜杠都不行。
  • Query 里的子资源参数必须参与签名。
  • 最快的定位办法:打印 StringToSign,和一次手动 curl 请求对照。
本文转载自 Clara轻量论坛系统,原文地址:https://www.leleweb.cn/thread-630.html
转载请注明出处,版权归原作者所有。
他们都看过 1 人浏览过
不能说的秘密

全部回复 0

还没有回复,来抢沙发~