zl程序教程

您现在的位置是:首页 >  其他

当前栏目

PHP编码安全之四: URL跳转安全(漏洞)

2023-04-18 14:13:51 时间

URL跳转漏洞, 也叫开放重定向漏洞(open redirect)。 如果处理不当会导致用户被重定向至钓鱼或恶意网站。我们通常用白名单机制来处理,比如qq登录等接口的回调页面就要求做白名单限制。通常我们会判断跳转url中的域名或者路径是否合法,但是问题往往就出现在这些判断中。

漏洞通常发生在以下几个地方:

  1. 用户登录、统一身份认证处,认证完后的跳转;
  2. 用户分享、收藏内容过后的跳转;
  3. 跨站点认证、授权(第三方登录)后的跳转;
  4. 站内点击其它网址链接时的跳转。

出现漏洞的原因大概有以下5个:

  1. 写代码时没有考虑过任意URL跳转漏洞,或者根本不知道/不认为这是个漏洞;
  2. 写代码时考虑不周,用取子串、取后缀等方法简单判断,代码逻辑可被绕过;
  3. 对传入参数做一些特殊的操作(如域名剪切、拼接、重组)和判断,适得其反,反而被绕过;
  4. 原始语言自带的解析URL、判断域名的函数库出现逻辑漏洞或者意外特性,可被绕过;
  5. 原始语言、服务器/容器特性、浏览器等对标准URL协议解析处理等差异性导致被绕过。

在php中, 常用如下方法进行跳转(以参数名url为例):

$redirect_url = $_GET['url'];
header("Location: " . $redirect_url);

后面假设源域名为:www.example.com,我们先设置白名单:

$arr_white_list = ['localhost', 'example.com'];

域名字符串检测

使用stripos检测url字符串中是否存在于白名单中的域名 - 不安全

如果使用stripos检测字符串是否存在于跳转url中,是不可靠的,比如下面的url:

https://www.example.com/redirect.php?url=http://www.example.com.www.evil.com/untrust.html

代码:

$redirect_url = $_GET['url'];
$is_redirect_url_valid = false; //跳转url是否验证通过
$arr_white_list = ['localhost', 'example.com'];
foreach($arr_white_list AS $white_item){
    if(stripos($redirect_url, $white_item) !== false){
        $is_redirect_url_valid = true;
        break;
    }
}

上面这种简单的判断域名是否存在url中是不安全的,不要使用。

提取并检测域名是否在白名单中

使用内置函数parse_url来判断url格式是否正确,并提取url的各个部分。

这里我们只考虑给定的url是包含protocol的完整的URL。对严重不合格的 URL,parse_url() 可能会返回 FALSE。

$redirect_url = $_GET['url'];
$arr = parse_url($redirect_url); //解析 URL,返回其组成部分
$host = ''; //要跳转的URL的域名
if($arr !== null && $arr !== false) {
    if(array_key_exists('host', $arr)){
        $host = $arr['host'];
    }
}else{
    echo 'Error: url式不正确';
}
$is_redirect_url_valid = false;
$arr_white_list = ['localhost', 'example.com'];
if($host !== ''){
    //判断域名是否在白名单中(包含二级域名)
    foreach($arr_white_list AS $white_item){
        if(
            ($host === $white_item)
            ||
            (
                //子域名: 长度 > 主域名
                strlen($host) > strlen($white_item)
                &&
                substr($host, strlen($host)-strlen($white_item)-1) === '.'.$white_item
            )
        ){
            $is_redirect_url_valid = true;
            break;
        }
    }
}

var_dump($is_redirect_url_valid); //打印跳转url检查是否通过

上面的代码简单演示了后端PHP如何判断跳转url能否通过验证。

如果白名单下的页面有重定向漏洞,这里就无法处理。我们至少要确保站内没有重定向漏洞。

防护方法

  1. 固定跳转地址,不让用户控制变量。 这种不适合登录后返回原来的页面这种场景,但是我们可以把登录功能作为页面公共部分, 所有页面都可以直接调出登录窗口,登录成功后直接reload页面。

  2. 跳转目标地址采用白名单映射机制, 不在白名单内的跳转到固定地址或不做任何动作。

  3. 合理充分的校验跳转的目标地址,非己方地址时告知用户跳转风险。

参考:

https://landgrey.me/open-redirect-bypass/

https://www.cnblogs.com/xiaozi/p/10682199.html

http://www.apgy.club/temp/url.html

https://www.anquanke.com/post/id/94377