今天顺手记一笔:每日大赛91:跳转风险怎么避我用最短路径讲清楚

跳转(redirect)看起来简单:用户点个链接,服务器或页面把人丢到另一个 URL。但是,稍不注意就会变成“开放重定向”“钓鱼中转”或“流量劫持”的入口。下面用最短路径把风险、常见坑和可落地的防护措施讲清楚——直接实用,便于马上落地。
一句话定义
- 跳转风险(open redirect):应用把外部提供的 URL 原样跳转,攻击者借此把用户导向恶意站点,配合社会工程学就能骗取凭证、窃取 cookie 或传播恶意软件下载。
典型攻击场景(举几个能马上懂的)
- 登录成功后用 returnurl 参数直接跳转,攻击者把参数设为钓鱼页:site.com/login?returnurl=https://evil.com
- 第三方分享短链接或邮件里嵌入看似正常的中转域名,实际最终到恶站
- OAuth 类回调中 state/redirect_uri 未校验,导致授权码被窃取或被重定向到非授权回调
核心防护原则(三个最短路径) 1) 只允许已知/受信任的目标(白名单或映射表) 2) 不直接信任前端/URL 参数,做严格校验和归一化 3) 在必须转发外部 URL 时,展示中间确认页或对目标做签名/校验
可直接落地的技术措施(按优先级)
- 使用白名单或 ID 映射
- 不接受 arbitrary URL。把可跳转目标列成白名单,或者让前端只传一个 key(如 next=dashboard),后端把 key 映射到具体 URL。
- 禁止以用户输入直接作为 Location
- 任何基于参数的跳转都先 parse、normalize、验证 host、scheme(只允许 http/https)和 port。
- 校验 host 和域名
- 精确匹配域名(例:allowed = ["example.com","app.example.com"]),不要用 contains 或 endsWith 陷阱(evil-example.com)
- 防止 IDN/拼音混淆(punycode),对国际化域名做 toASCII 后比较
- 阻断危险 scheme
- 拒绝 javascript:, data:, file:, vbscript: 等
- 使用签名化的跳转 token(短期有效)
- 当必须支持任意 URL(比如分享链路),签名 URL 或返回 token,并在跳转时验证签名和时间戳(HMAC)。
- 中间确认页(interstitial)
- 跳转前先展示“你即将离开 example.com,前往:evil.com,是否继续?”,必要时告诉用户为什么要跳。
- 在邮件和短链接中避免中转域名显式隐藏最终目的地
- 让链接显示真实域名,或在邮件中明确告知跳转到第三方的风险。
- 安全头与浏览器策略辅助
- 设置 SameSite cookie、Content-Security-Policy、Referrer-Policy(在某些场景下限制敏感数据泄露)。
- 测试与扫描
- 自动化扫描器和手工测试结合,尝试 payload: //evil.com http://evil.com@trusted.com http://trusted.com.evil.com //evil.com/path /%2F%2Fevil.com javascript:alert(1)
- 日志告警
- 对带外域名跳转的请求做日志和告警,便于追踪异常使用。
简短代码示例(思想胜于语言,下面展示映射策略的伪码)
-
映射白名单(伪代码) allowed = { "dashboard": "https://example.com/dashboard", "profile": "https://example.com/profile", "help": "https://help.example.com/" } key = request.getParam("next") if key in allowed: redirect(allowed[key]) else: redirect("/")
-
HMAC 签名跳转(简要流程) 1) 生成链接时:payload = url + "|" + expiry; signature = HMAC(secret, payload); store or embed as ?url=ENC(url)&sig=signature&exp=timestamp 2) 跳转时:验证 sig 合法且未过期,再跳。
常见易犯的坑(看一眼就改)
- 用 startsWith 或 contains 判断 host(会被 subdomain 混淆)
- 接受 URL 编码或双重编码导致绕过检查
- 允许协议相对 URL(//evil.com)而只检查 host
- 在链接中通过 @ 符号混淆(http://evil.com@trusted.com)
- 忽略端口号或使用 port 跳转绕过规则
产品层面的权衡(短评)
- 白名单最安全但灵活性低;签名 token 灵活且安全;中间页体验差但可作为临时补救措施。
- 根据业务选择最简单可行的防护:大多数 web app 用映射/白名单即可覆盖 99% 风险。
结尾行动清单(3 步马上做) 1) 在代码中查找所有使用 redirect/Location 的点,找出直接使用外部输入的地方。 2) 对这些地方先临时加上中间确认页或拒绝外部 URL,长期方案迁移到白名单或签名机制。 3) 编写自动化测试用例,包含常见绕过 payload,并在 CI 中持续运行。

