开放重定向是什么?前端跳转参数如何避免被钓鱼利用?
简化版
开放重定向是指页面根据用户可控参数直接跳转到任意 URL,例如 ?redirect=https://evil.com,攻击者可以借可信域名外壳诱导用户进入钓鱼站。前端常见场景是登录后回跳、支付完成回跳、活动页跳转。防范方式是只允许站内相对路径或白名单域名,解析 URL 后校验 origin,拒绝协议绕过、双斜杠、编码绕过和 javascript: URL。
详细版
危险代码很常见:
const url = new URLSearchParams(location.search).get('redirect')
if (url) location.href = url
更安全的做法是限制跳转目标。
| 风险输入 | 问题 |
|---|---|
https://evil.com | 直接跳外站 |
//evil.com | 协议相对 URL |
javascript:alert(1) | 脚本协议 |
| 编码后的外链 | 绕过简单 startsWith |
可信域名上的开放重定向,是钓鱼链接最喜欢借用的“门面”。
完整版教学
一、开放重定向为什么危险
用户看到链接域名是可信站点,例如 https://bank.example.com/login?redirect=...,更容易点击。页面加载后再跳到恶意站,用户可能以为仍在可信流程里。
攻击者可以用它做钓鱼、盗号、恶意下载和 OAuth 流程污染。
二、常见业务场景
前端经常需要回跳:
- 登录成功回到原页面。
- 支付完成回到订单页。
- 邀请链接跳活动页。
- 权限不足跳登录页。
这些场景合理,但跳转目标不能完全由用户决定。
三、只允许相对路径
最简单安全策略是只允许站内相对路径。
function safeRedirect(target: string | null) {
if (!target || !target.startsWith('/')) return '/'
if (target.startsWith('//')) return '/'
return target
}
这样外部域名不会被允许。但还要避免反斜杠、编码等特殊绕过,复杂场景建议用 URL 解析。
四、白名单域名校验
如果确实要跳外部合作域名,应使用白名单。
function isAllowedUrl(raw: string) {
const url = new URL(raw, location.origin)
const allowed = new Set([location.origin, 'https://partner.example.com'])
return ['http:', 'https:'].includes(url.protocol) && allowed.has(url.origin)
}
不要用简单字符串包含判断,例如 url.includes('example.com'),会被 evil-example.com 或用户名字段绕过。
五、协议和编码绕过
攻击者会尝试各种形式:
| 绕过 | 示例 |
|---|---|
| 协议相对 | //evil.com |
| 用户名混淆 | https://example.com@evil.com |
| 编码 | %2f%2fevil.com |
| 脚本协议 | javascript:alert(1) |
所以要规范化解析后再判断。
六、OAuth 场景更敏感
OAuth 的 redirect_uri 必须由授权服务器严格白名单校验。前端自己也不要把登录回跳和 OAuth redirect_uri 混为一谈。
开放重定向如果出现在 OAuth 回调域上,可能导致授权码或 token 泄露。
七、常见误区与追问
- 误区:跳转是前端行为,不算安全漏洞。 钓鱼、授权码泄露和恶意下载都可能借开放重定向发生。
- 误区:startsWith 域名就安全。 URL 有用户名、编码、协议相对等绕过方式。
- 误区:只要是 HTTPS 外链就可以跳。 HTTPS 只保证传输加密,不代表目标可信。
- 追问:登录后回跳怎么做? 优先只保存站内相对路径,不接受完整外链。
- 追问:白名单应该怎么写? 解析 URL 后比对精确 origin,不用 contains。
- 追问:为什么
//evil.com危险? 它是协议相对 URL,浏览器会按当前协议跳到外部域名。
八、加强记忆
开放重定向记成“可信站帮坏站带路”。回跳参数只认站内相对路径,必须外跳时解析 URL 后比对白名单 origin,拒绝奇怪协议和编码绕过。