Go的http.Client默认重定向存在SSRF、循环跳转等风险,因最多自动跳转10次且不校验目标可信性;必须通过自定义CheckRedirect函数控制逻辑,返回http.ErrUseLastResponse可安全终止并保留响应体。
Go 的 http.Client 默认最多重定向 10 次,且不校验重定向目标是否可信。遇到恶意响应(比如 Location: javascript:alert(1))、循环重定向、或需要跳过某些中间跳转时,CheckRedirect 是唯一可控入口。它不是“开关”,而是一个回调函数——你得自己决定某次重定向该放行、该终止,还是该改写请求。
必须把函数赋给 Client.CheckRedirect 字段,否则走默认逻辑。函数签名固定:func(req *http.Request, via []*http.Request) error。其中 req 是即将发出的重定向请求,via 是已发出的请求链(含原始请求),长度即重定向次数。
nil:允许本次重定向nil 错误:中止整个请求,Do() 返回该错误req.URL 或 req.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 包含的是“已发出”的请求,不包含当前 req;所以 len(via) == 0 表示这是第一次重定向(原始请求是第 0 个)。容易错当成“已重定向次数”。
CheckRedirect 里读 req.Body:此时 body 已被 client 内部读取并丢弃,再读会得到空或 panichttp.Client 实例处理不同安全策略的请求:CheckRedirect 是实例级配置,混用易出错Client.Jar 已设置,否则重定向时 cookie 不会自动附加设 CheckRedirect: http.ErrUseLastResponse 可禁用所有重定向,但很多场景不能简单禁用:比如 OAuth 授权码流程必须跳转到第三方域名,又需限制只允许跳转到预设白名单域名;或者 API 网关返回 302 到内部服务,但路径需重写。这时候就得在回调里做判断和改写。
req.URL.Host 是否在允许列表中req.URL.Path 中的 .. 或空段标准化X-Forwarded-For 或 tokenAuthorization,除非目标域名明确信任真正难的不是写逻辑,而是厘清业务中哪些跳转是“必须放行”的、哪些是“必须拦截”的、哪些是“要改写后再放行”的——这些边界一旦模糊,CheckRedirect 就成了漏洞入口。