只要跳转目标能被用户参数控制,就别直接拿去做页面跳转。
一、风险现场:redirect_url 直接进 header() 会放开外链

登录后回到上一页、支付完成回到订单页、活动页跳回来源页,这些场景都会用到跳转参数。功能看起来很小,但如果参数没管住,就会变成开放重定向。
危险写法通常长这样:
$url = $_GET['redirect_url'] ?? '/';
header('Location: ' . $url);
exit;
这段代码的问题不是 header(),而是跳转目标完全由用户控制。攻击者把链接改成外部站点,用户登录后就被带走,页面看起来还像从正常站点跳出去的。
排查时重点看这些入口:
· redirect、redirect_url、return_url 这类参数。
· 登录、注册、绑定账号后的跳转。
· 支付、授权、第三方回调后的跳转。
只要参数能控制目标地址,就要进入校验链路。
二、处理链路:parse_url() 只负责拆 URL,不负责判断安全

parse_url() 很容易被误会成“URL 校验函数”。实际上它只是把 URL 拆成 scheme、host、path 等部分,不保证这个 URL 适合业务跳转。
更稳的处理顺序是:
· 用 filter_var() 和 FILTER_VALIDATE_URL 判断完整 URL 格式。
· 用 parse_url() 拆出 scheme 和 host。
· 只允许 https 或团队明确允许的协议。
· host 必须命中业务白名单。
· 不通过就跳默认安全路径,比如 /dashboard。
$redirect = $_GET['redirect_url'] ?? '/dashboard';
if (filter_var($redirect, FILTER_VALIDATE_URL) === false) {
$redirect = '/dashboard';
}
如果只允许站内路径,事情更简单:只接受以 / 开头的相对路径,并拒绝 //evil.example 这种协议相对 URL。
三、域名白名单:判断 host 要严格比较,别用包含判断

域名白名单最常见的坑,是用字符串包含判断。比如看到 example.com 就放行,结果 example.com.evil.test 也能过。
建议把允许的域名写成明确列表:
$allowedHosts = ['example.com', 'admin.example.com'];
$parts = parse_url($redirect);
$host = strtolower($parts['host'] ?? '');
if (!in_array($host, $allowedHosts, true)) {
$redirect = '/dashboard';
}
如果要允许子域名,也别用随手的 strpos()。至少要确认结尾边界,比如 .example.com,并把主域名单独列出来。
还有两个小细节:
· parse_url() 返回 false 时,要直接走默认路径。
· 跳转前可以把最终地址写日志,方便排查异常来源。
跳转功能本身不复杂,复杂的是“看起来像本域”的输入。解析、格式校验、白名单这三步分开写,后面维护会清楚很多。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











