开放重定向是指以你的真域名开头的 URL,会把访问者转到完全不同的站点。下面讲解其原理和可靠的防御方法(不会写攻击步骤)。
为什么危险
危险的本质在于「对域名的信任被滥用」这一点。
| 被滥用的方式 | 会发生什么 |
|---|---|
| 借你的域名做钓鱼 | 链接开头是真域名→用户和过滤器都信任→被诱导到假站点 |
| 认证流程被劫持 | 篡改 OAuth 等的重定向目标,盯上 token/code 的传递 |
| 放大其他漏洞 | 与 SSRF 或 XSS 组合,扩大危害 |
为什么会成立(原理)
在「登录后回到原来的页面」之类的功能里,如果原样信任并转发返回地址 URL,就会发生。用户看到的 URL 的「开头」是真域名,所以很难一直怀疑到最后,这正是麻烦之处。
防御:不接收外部 URL
跳转目标只允许相对路径(最重要)
返回地址只接受 /dashboard 这样的相对路径,拒绝完整 URL(以 http://、// 开头的)。不给用户留下任何能指定跳转目标域名的余地。
对照允许列表校验
如果需要动态的跳转目标,就只对已知跳转目标的固定集合(映射表)放行。不匹配就回到默认页面。
不要依赖字符串判断
//evil、反斜杠、@、多重编码等绕过手法很多。改变接收方式的设计(相对路径+允许列表)比一条条往过滤器里追加规则更可靠。
实在要跳到外部就明确告知
如果跳到外部是规格需要,就插入一个确认页面,向用户显示「即将跳转到外部站点」。不要悄无声息地跳走。
本站的视角:单独看评级为「低」,但和钓鱼组合就很严重
开放重定向单独看时往往被认为严重度低,但因为攻击者可以借用你域名的信任,一旦和钓鱼或认证劫持组合起来,就会变得很严重。正因如此,用「相对路径+允许列表」这种设计层面的修复来消除原因才高效。它和 SSRF(让服务器去访问任意目标)的思路相似,二者共同的防御原则都是「不让用户来决定跳转目标・连接目标」。
开放重定向、SSRF 与钓鱼的区别
开放重定向本身与其说是攻击,不如说是一种「让其他攻击更可信」的缺陷。把它和相关术语并列,就能看清它的位置。
| 术语 | 会发生什么 | 由谁修复 |
|---|---|---|
| 开放重定向 | 真站点的 URL 把用户的浏览器转发到外部站点 | 拥有重定向功能的网站开发者 |
| SSRF | 不是浏览器,而是服务器向被指定的目标发送请求 | 拥有 URL 获取功能的网站开发者 |
| Phishing(网络钓鱼) | 用假站点诱导人输入 ID、密码或银行卡信息 | 用户、邮件接收方,以及被冒充的组织 |
当钓鱼链接能用上真域名时,用户和邮件过滤器都更难识破。消除开放重定向,就能防止自己的域名被用于诈骗。
实例:影响最大的是登录集成(OAuth)
开放重定向影响最大的地方是登录集成(OAuth)周边。授权完成后,用户会带着授权码或 token 被送回已登记的重定向 URI。如果返回的网站存在开放重定向,授权码或 token 就可能被继续送到外部站点。
因此,OAuth 2.0 安全最佳当前实践(RFC 9700,2025 年 1 月)要求以下两点。
- 授权服务器以完全一致的方式,把重定向 URI 与预先登记的值进行比对
- 客户端和授权服务器都不提供「按查询参数中的 URL 转发到任意地址」的页面
开放重定向被归类为 CWE-601。单独看时很少被评为高严重度,但一旦挨着登录集成,就会成为严重问题。
严重程度
单独来看,开放重定向通常被评为中等。常见的评分是 CVSS 3.1 6.1。
- 需要用户打开链接(UI:R)
- 影响是把用户送到别的站点,不能让人直接读取网站的数据
- 由于影响超出了进行重定向的网站本身,因此按范围变更(S:C)计分
不要因为分数而拖延。如果它位于登录集成的返回路径上,或者已经被用于钓鱼,就优先修复。要为自己的情况评分,可以使用 CVSS 计算器。
框架已有的安全辅助功能
框架的功能比自己写的字符串检查更不容易漏掉情况。两个常见的例子:
| 框架 | 辅助功能 | 作用 |
|---|---|---|
| Ruby on Rails | redirect_to 的默认行为和 url_from | 默认阻止重定向到其他主机;要把用户送到站外,必须显式指定 allow_other_host: true。url_from 只在 URL 属于同一主机时返回该 URL,否则返回 nil |
| ASP.NET Core | LocalRedirect 和 Url.IsLocalUrl | 只重定向到同一站点内的 URL,遇到外部 URL 会抛出异常 |
Rails 文档提醒,即使使用 allow_other_host: true,把用户提供的参数直接传进去也是不安全的。添加站外重定向功能时,要避免这种组合。
检查自己网站的方法
找出接收跳转目标的参数
在代码和 URL 列表中搜索名为 next、url、redirect、return_to、continue、callback 等的参数,确认它们是否被原样交给了重定向处理。
确认跳转目标的校验方式
看校验是否依赖前缀字符串匹配或「是否以 http 开头」。应改为用 URL 解析器把 URL 拆成协议、主机和端口后再对照允许列表,或者使用框架提供的安全辅助函数(例如 ASP.NET Core 的 LocalRedirect)。
检查登录集成的登记内容
在 OAuth 或 SSO 的设置中,确认已登记的重定向 URI 里没有通配符,也没有已不再需要的旧 URL。
考虑用 ID 来选择跳转目标
如果必须跳到外部,就不要接收 URL 本身,而是接收一个短 ID,再在服务器端的对照表里查出跳转目标。OWASP 推荐这种做法。
在访问日志中查找站外重定向
在访问日志中搜索跳转目标参数里含有自己以外主机名的请求。如果找到,就确认该重定向是否真的把人送到了站外,并把它当作有人可能在散布这类链接的迹象。
参考资料(一手来源)
- OWASP, "Unvalidated Redirects and Forwards Cheat Sheet" — cheatsheetseries.owasp.org(在服务器端映射 ID、对解析后的 URL 做允许列表校验、警告页面)
- IETF RFC 9700, "Best Current Practice for OAuth 2.0 Security"(2025 年 1 月)— datatracker.ietf.org(重定向 URI 的完全一致比对、禁止开放重定向器)
- Ruby on Rails API, "ActionController::Redirecting" — api.rubyonrails.org(默认防止重定向到其他主机、
allow_other_host、url_from) - Microsoft, "Prevent open redirect attacks in ASP.NET Core" — learn.microsoft.com(
LocalRedirect、IsLocalUrl、记录非本地 URL) - MITRE, "CWE-601: URL Redirection to Untrusted Site ('Open Redirect')" — cwe.mitre.org