Beego中间件需在BeforeExec阶段拦截非法重定向请求,通过注册beego.InsertFilter("/login", beego.BeforeExec, checkRedirectParam)精确匹配路径,解析并校验redirect参数的Scheme、Host及安全性,非法时调用ctx.AbortWithStatusJSON(400)终止。

Beego 中间件如何识别并拦截非法重定向请求
Beego 本身不自动拦截重定向(301/302)响应,所谓“非法重定向”实际是指:攻击者构造恶意 Redirect 跳转(如开放重定向漏洞),或业务逻辑中误将未授权路径重定向到登录页却漏校验目标地址。中间件需在重定向发生前介入,而不是事后捕获响应状态码。
- 重定向动作通常发生在 Controller 的
this.Redirect()或中间件的ctx.Redirect()中,此时响应尚未写出,但LocationHeader 已可被篡改或伪造 - 真正可拦截的时机是
BeforeExec阶段——此时路由已匹配、Controller 即将执行,但任何Redirect还未触发;若你把校验逻辑放在Prepare()里,可能因 Controller 主动调用Redirect而绕过 - 关键不是“拦截重定向”,而是“拦截跳转目标”:检查
ctx.Input.Query("redirect")、ctx.Input.Param("next")或表单字段中的跳转 URL 是否属于白名单
怎么用 InsertFilter 注册只作用于重定向相关路径的中间件
别用全局过滤器去扫所有请求。开放重定向漏洞高发点集中在 /login、/oauth/callback、/reset 这类带 redirect 参数的入口。注册时应精确限定路径前缀:
- 写成
beego.InsertFilter("/login", beego.BeforeExec, checkRedirectParam),而不是/*—— 后者会让每个静态资源都进一次校验,徒增开销 - 路径结尾不加斜杠:
/login可匹配/login?redirect=...和/login/,而/login/会漏掉无尾缀的请求 - 若需同时覆盖
/oauth/callback和/sso/login,得分别注册两次,Beego 不支持多路径逗号分隔
中间件内如何安全校验 redirect 参数是否合法
不能只做 strings.HasPrefix(redirectURL, "https://mydomain.com"),攻击者可构造 https://mydomain.com@evil.com 绕过。必须解析 URL 并比对 Host + Scheme:
- 用
url.Parse(redirectURL)解析,检查err == nil && u.Scheme == "https" && u.Host == "mydomain.com" - 禁止允许空
Host或u.Opaque != ""(如//evil.com或javascript:alert(1)) - 若业务允许相对路径(如
/dashboard),需用url.JoinPath(ctx.Request.URL.Path, redirectURL)归一化后再判断是否以/开头且不含.. - 发现非法值时,必须用
ctx.AbortWithStatusJSON(400, map[string]string{"error": "invalid redirect"})终止,而非ctx.Redirect(302, "/")—— 后者仍是重定向,只是换了个目标
为什么 AfterExec 或 FinishRouter 阶段无法拦截重定向
因为 Beego 在 AfterExec 执行前,已调用 ctx.ResponseWriter.WriteHeader() 和 Write() 发送了 302 响应头。此时再调 Abort() 或写入新内容,客户端早已收到跳转指令,后端修改无效。
-
AfterExec只适合日志、指标上报等副作用操作,不能改变响应结果 -
FinishRouter更晚,甚至可能在 HTTP 连接关闭后才触发,完全不可控 - 真正能干预重定向的只有
BeforeExec(阻断 Controller 执行)和BeforeRouter(连路由都不让进,适合全站统一跳转规则)
最容易被忽略的是:Beego 的 ctx.Redirect() 默认不校验目标地址,也不拒绝协议非 http/https 的跳转。只要没在 BeforeExec 阶段提前解析并验证参数,就等于把重定向控制权交给了前端传入的任意字符串。











