登录成功后的跳转地址由参数带来,不校验会被拿去做什么
登录成功后的跳转地址由参数带来,这件事本身是合理设计:用户从某个页面被要求登录,登录后应回到原处。问题在于这个参数完全由外部提供,不做校验时,攻击者要做的只是把它换成自己的地址。这类缺陷通常被称为开放重定向,它不改一行代码逻辑,只是把「站内地址」换成「任意地址」。
不做校验会被拿去做什么
第一种用法是钓鱼跳板。链接的域名是真的,路径与查询串是假的,用户在地址栏看到熟悉的主域,登录页也确实是真的,只是登录完成后落到一个仿制的页面上,那一页继续索要二次验证信息或诱导下载。因为发起跳转的是可信站点本身,链接在邮件与即时通讯里的信誉判定也更容易通过。
第二种用法是配合脚本协议。跳转取值若允许脚本前缀,页面并不离开当前域,却在当前域里执行了外部提供的代码,后果由登录后的会话承担。公开版本记录里能看到这两件事被写进同一条修复:PbootCMS 在 V3.2.24 记录「修复会员登录 backurl 开放重定向及 javascript: XSS,集中校验并安全输出跳转 URL」。
第三种用法是绕过审计。有些站点把登录后的行为统计、来源判断挂在跳转参数上,参数被替换意味着后续动作也落到了错误的上下文里。
集中校验该挡哪几类取值
校验点要集中,不要散在模板里。模板可以各写各的样式,跳转地址的判断只能有一处。
需要挡掉的取值大致四类:
- 绝对地址:带协议与主机的外部地址,一律要求回到白名单内的域名;同域下的不同子域也要显式列出,别靠后缀匹配。
- 协议相对写法:以双斜杠开头的写法会被解析成外部主机,这是最常见的漏点。
- 脚本前缀:任何以脚本协议开头的值都不应成为跳转目标,直接丢弃并回到默认地址。
- 编码变形:多层百分号编码、空白字符与控制符拼出来的变相写法,要先归一化再判断,判断完再输出,输出的时候按目标上下文重新编码。
校验通过之后还有一步:安全地输出这个地址。把已经判过的值直接拼进脚本或属性里,等于把前面的校验作废;AnQiCMS 在内容层面对 SQL 注入与跨站脚本做了防御,并内置 JWT 认证保证会话标识不被伪造,这两条与跳转校验解决的是不同层面的问题,缺一不可。
登录链路上的另外两道防线
跳转校验管的是「登录之后去哪里」,还有两段要管。表单侧接入 reCAPTCHA 验证码,用来区分真人与批量脚本,它挡的是自动化尝试,不替代口令校验;再往里一层是频率控制与失败次数限制,避免同一批账号被反复试探。
需要澄清一件事:防采集与干扰码针对的是内容被批量搬运,和登录回跳不是同一条链路,别把两者当成同一类防护来配置。
常见问题
只用相对路径不就够了吗? 相对路径能挡掉外部主机,但挡不住站内跳转后页面里注入的内容;仍要校验路径段,避免落在意料之外的页面上。
白名单该配在哪一层? 配置在项目或站点设置里,由程序读取,不要写死在模板中。多站点场景下每个站点的可回跳域不同,白名单应随站点走。
校验完还要求登录态吗? 要。跳转地址合法不代表当前会话有权访问目标页面,两件事分别判断。