一聚教程网:一个值得你收藏的教程网站

热门教程

怎样在 Go 中使用 net/http.Client.CheckRedirect 控制重定向行为

时间:2026-06-23 08:34:46 编辑:袖梨 来源:一聚教程网

Go的http.Client默认重定向存在SSRF、循环跳转等风险,因最多自动跳转10次且不校验目标可信性;必须通过自定义CheckRedirect函数控制逻辑,返回http.ErrUseLastResponse可安全终止并保留响应体。

为什么默认重定向会出问题

Go 的 http.Client 默认最多重定向 10 次,且不校验重定向目标是否可信。遇到恶意响应(比如 Location: javascript:alert(1))、循环重定向、或需要跳过某些中间跳转时,CheckRedirect 是唯一可控入口。它不是“开关”,而是一个回调函数——你得自己决定某次重定向该放行、该终止,还是该改写请求。

如何定义 CheckRedirect 函数并赋值给 Client

必须把函数赋给 Client.CheckRedirect 字段,否则走默认逻辑。函数签名固定:func(req *http.Request, via []*http.Request) error。其中 req 是即将发出的重定向请求,via 是已发出的请求链(含原始请求),长度即重定向次数。

  • 返回 nil:允许本次重定向
  • 返回非 nil 错误:中止整个请求,Do() 返回该错误
  • 可修改 req.URLreq.Header:影响下一次请求(比如补 Authorization)
  • 注意:不能在函数里调用 req.Body.Close(),body 由 client 自动管理

示例:

client := &http.Client{    CheckRedirect: func(req *http.Request, via []*http.Request) error {        if len(via) >= 3 {            return errors.New("too many redirects")        }        if req.URL.Scheme != "https" {            return fmt.Errorf("redirect to insecure scheme: %s", req.URL.Scheme)        }        return nil    },}

常见陷阱:via 参数被误读、body 被提前消费

via 包含的是“已发出”的请求,不包含当前 req;所以 len(via) == 0 表示这是第一次重定向(原始请求是第 0 个)。容易错当成“已重定向次数”。

  • 别在 CheckRedirect 里读 req.Body:此时 body 已被 client 内部读取并丢弃,再读会得到空或 panic
  • 别复用同一个 http.Client 实例处理不同安全策略的请求:CheckRedirect 是实例级配置,混用易出错
  • 如果需要携带 cookie,确保 Client.Jar 已设置,否则重定向时 cookie 不会自动附加

何时必须自定义 CheckRedirect 而不是禁用重定向

CheckRedirect: http.ErrUseLastResponse 可禁用所有重定向,但很多场景不能简单禁用:比如 OAuth 授权码流程必须跳转到第三方域名,又需限制只允许跳转到预设白名单域名;或者 API 网关返回 302 到内部服务,但路径需重写。这时候就得在回调里做判断和改写。

  • 白名单校验:检查 req.URL.Host 是否在允许列表中
  • 路径清理:把 req.URL.Path 中的 .. 或空段标准化
  • Header 注入:对特定域名添加 X-Forwarded-For 或 token
  • 避免泄露敏感头:清除原始请求中的 Authorization,除非目标域名明确信任

真正难的不是写逻辑,而是厘清业务中哪些跳转是“必须放行”的、哪些是“必须拦截”的、哪些是“要改写后再放行”的——这些边界一旦模糊,CheckRedirect 就成了漏洞入口。

热门栏目