KernelRanch
返回文章列表

回跳地址通过了前缀检查:用 URL 解析结果校验来源

2026-11-13 #javascript#url#security

登录后跳回 return_to 指定地址。用字符串前缀检查 https://app.example.com 时,域名后缀和 URL 用户信息都可能改变浏览器实际访问的主机。先按 URL 标准解析,再比较解析后的 origin,并把审核过的序列化结果交给重定向。

1. 用反例测试允许列表

保存为 origin_lab.mjs,运行 node origin_lab.mjs。所有输入仅用于解析。

import assert from 'node:assert/strict';

const app = new URL('https://app.example.com/');
function validatedTarget(input) {
  let url;
  try {
    url = new URL(input, app);
  } catch {
    return null;
  }
  if (url.protocol !== 'https:' || url.origin !== app.origin ||
      url.username || url.password) {
    return null;
  }
  return url.href;
}

const cases = [
  ['/orders', 'https://app.example.com/orders'],
  ['https://APP.example.com:443/orders', 'https://app.example.com/orders'],
  ['https://app.example.com.evil.example/orders', null],
  ['https://app.example.com@evil.example/orders', null],
  ['//evil.example/orders', null],
  ['https://app.example.com:8443/orders', null],
  ['javascript:alert(1)', null],
  ['blob:https://app.example.com/id', null],
];
for (const [value, expected] of cases) {
  const target = validatedTarget(value);
  assert.equal(target, expected);
  console.log(value, '=>', target ?? 'rejected');
}
assert.equal(cases[2][0].startsWith(app.origin), true);
assert.equal(cases[3][0].startsWith(app.origin), true);

本机实测输出如下。

/orders => https://app.example.com/orders
https://APP.example.com:443/orders => https://app.example.com/orders
https://app.example.com.evil.example/orders => rejected
https://app.example.com@evil.example/orders => rejected
//evil.example/orders => rejected
https://app.example.com:8443/orders => rejected
javascript:alert(1) => rejected
blob:https://app.example.com/id => rejected

2. 区分来源限制和页面限制

origin 包含协议、主机和端口,默认端口经过规范化。只比较 hostname 会同时放过使用其他协议或非默认端口的目标。本例额外限制协议,避免 origin 继承自内部 URL 的 blob 地址通过。用户信息段也被明确拒绝。

如果登录回跳只能去少数业务页面,可以直接接受一个路由名,从服务端固定表生成目标地址。必须支持 URL 时,在解析后比较 origin,再校验允许的路径。只校验来源仍会允许同源站点上的其他路径,包括可继续重定向的入口。

小结

校验与实际重定向共用一次 URL 解析。测试域名后缀、用户信息、协议相对地址与非默认端口,保存通过检查的序列化 URL。

数据来源与检索时间

检索时间 2026-11-13。

来源URL对应内容
WHATWG URLhttps://url.spec.whatwg.org/#url-parsingURL 解析与规范化
WHATWG URLhttps://url.spec.whatwg.org/#originorigin 的计算和 blob 行为

核验说明

  • Node.js v24.19.0 验证 URL 解析和校验结果,未访问示例地址。