深入理解 `wp_safe_redirect()` 函数的源码,它是如何通过 `wp_redirect_location` 过滤器允许开发者自定义重定向白名单的?

各位观众老爷,大家好!我是今天的讲师,代号“零误差”。今天咱们来聊聊WordPress里一个看似简单,实则暗藏玄机的函数:wp_safe_redirect()

这玩意儿,说白了就是个“安全版”的 header('Location: ...')。但是!它比直接用 header() 多了一层保护,防止你一不小心把用户重定向到恶意网站,导致菊花不保(数据安全,懂的都懂)。

咱们今天重点不是讲它怎么用,而是深入扒一扒它的源码,看看它是如何通过 wp_redirect_location 过滤器,让开发者能够自定义重定向白名单,让安全更有弹性。

第一幕:wp_safe_redirect() 的核心逻辑

首先,咱们先来看看 wp_safe_redirect() 的源码(为了方便讲解,我做了一些简化,只保留核心部分):

function wp_safe_redirect( $location, $status = 302 ) {
    $location = wp_sanitize_redirect( $location ); // 第一道防线:清理location

    if ( ! wp_is_safe_redirect( $location ) ) { // 第二道防线:安全检查
        $location = wp_get_referer(); // 重定向到来源页,防止恶意跳转

        if ( ! $location ) {
            $location = home_url(); // 实在不行,重定向到首页
        }
    }

    wp_redirect( $location, $status );
    exit;
}

从代码里,我们可以看到几个关键步骤:

  1. wp_sanitize_redirect( $location ) 这是一个“预处理”函数,负责清理 location 变量,防止注入攻击。它会移除一些危险字符,确保 location 是一个相对安全的 URL。
  2. wp_is_safe_redirect( $location ) 这是整个安全机制的核心。它负责判断 location 是否在一个允许的白名单里。如果不在白名单里,就认为是不安全的重定向。
  3. 备选方案: 如果 location 不安全,就尝试重定向到用户之前的页面 (wp_get_referer()),如果连之前的页面也找不到,那就直接重定向到网站首页 (home_url())。
  4. wp_redirect( $location, $status ) 如果 location 通过了安全检查,就执行真正的重定向操作。
  5. exit; 重定向之后,一定要终止脚本执行,防止后续代码继续运行,造成不可预知的错误。

第二幕:wp_is_safe_redirect() 的白名单机制

接下来,咱们重点看看 wp_is_safe_redirect() 这个函数。它才是决定重定向是否安全的“裁判”。

function wp_is_safe_redirect( $location ) {
    $home        = get_option( 'home' );
    $siteurl     = get_option( 'siteurl' );

    $is_safe = false;

    // 1. 检查是否为空
    if ( empty( $location ) ) {
        return false;
    }

    // 2. 检查是否是站内链接
    if ( wp_http_validate_url( $location ) ) {
        $home_url_host    = parse_url( $home, PHP_URL_HOST );
        $siteurl_host = parse_url( $siteurl, PHP_URL_HOST );
        $location_host  = parse_url( $location, PHP_URL_HOST );

        if ( $location_host === $home_url_host || $location_host === $siteurl_host) {
            $is_safe = true;
        } else {
            $is_safe = false;
        }

    } else {
        // 相对路径,认为是安全的
        $is_safe = true;
    }

    /**
     * Filters whether the redirect location is safe.
     *
     * @since 3.0.0
     *
     * @param bool   $is_safe  Whether the redirect location is safe.
     * @param string $location The redirect location.
     */
    return apply_filters( 'wp_safe_redirect_location', $is_safe, $location );
}

这个函数的大致逻辑是:

  1. 获取站点 URL: 先获取网站的 homesiteurl 选项,这两个选项通常指向你的网站域名。
  2. 判断是否为空: 如果location 是空字符串, 直接返回false。
  3. 判断是否是站内链接: 使用 wp_http_validate_url() 判断 location 是否是一个完整的 URL。如果是,就比较 location 的域名和站点域名是否一致。如果一致,就认为是安全的。
  4. 相对路径: 如果 location 不是一个完整的 URL,而是相对路径,就认为是安全的。
  5. wp_safe_redirect_location 过滤器: 最后,也是最重要的,使用 apply_filters() 应用 wp_safe_redirect_location 过滤器。这个过滤器允许开发者自定义重定向白名单,覆盖默认的安全判断逻辑。

第三幕:wp_redirect_location 过滤器的妙用

现在,咱们来重点说说 wp_redirect_location 过滤器。这玩意儿才是今天的主角!

这个过滤器允许你编写自己的函数,来判断 location 是否安全。你的函数会接收两个参数:

  • $is_safe:一个布尔值,表示 WordPress 默认的安全判断结果。
  • $location:要重定向的 URL。

你的函数可以根据 $location 的值,返回 true (安全) 或 false (不安全),覆盖 WordPress 默认的判断逻辑。

举个例子,假设你想允许重定向到特定的几个外部域名,比如 example.comexample.org。你可以这样写:

add_filter( 'wp_safe_redirect_location', 'my_custom_safe_redirect', 10, 2 );

function my_custom_safe_redirect( $is_safe, $location ) {
    $allowed_domains = array(
        'example.com',
        'example.org',
    );

    $location_host = parse_url( $location, PHP_URL_HOST );

    if ( in_array( $location_host, $allowed_domains, true ) ) {
        return true; // 允许重定向到 example.com 和 example.org
    }

    return $is_safe; // 其他情况,沿用 WordPress 默认的安全判断
}

这段代码做了什么?

  1. add_filter() 首先,使用 add_filter() 注册一个过滤器,将 my_custom_safe_redirect() 函数绑定到 wp_safe_redirect_location 过滤器上。
    • 'wp_safe_redirect_location':过滤器名称。
    • 'my_custom_safe_redirect':你的自定义函数名称。
    • 10:优先级,数值越小,优先级越高。
    • 2:参数数量,表示你的函数会接收两个参数 ($is_safe$location)。
  2. my_custom_safe_redirect() 这是你的自定义函数。
    • 定义白名单: 首先,定义一个 $allowed_domains 数组,包含你允许重定向的域名。
    • 解析域名: 使用 parse_url() 函数解析 $location,获取它的域名。
    • 判断是否在白名单里: 使用 in_array() 函数判断 $location 的域名是否在 $allowed_domains 数组里。如果在,就返回 true,允许重定向。
    • 沿用默认判断: 如果 $location 的域名不在白名单里,就返回 $is_safe,沿用 WordPress 默认的安全判断逻辑。

表格总结:wp_safe_redirect_location 过滤器参数

参数名称 数据类型 描述 ——– ——– ——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————–0x1103702222

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注