深入理解 WordPress `wp_get_referer()` 函数源码:如何获取 `HTTP_REFERER` 并进行安全验证。

各位观众老爷,大家好!我是今天的讲师,江湖人称“代码老中医”,专治各种代码疑难杂症。今天咱们就来聊聊 WordPress 里面那个看似简单,实则暗藏玄机的 wp_get_referer() 函数。

这玩意儿,说白了,就是用来获取 HTTP_REFERER 的,但这可不是直接拿来就用那么简单。WordPress 作为全球最受欢迎的 CMS,安全性可是重中之重。所以,wp_get_referer() 在获取 HTTP_REFERER 的同时,还做了不少安全验证工作。

今天,咱就来扒一扒 wp_get_referer() 的源码,看看它到底是怎么工作的,以及我们应该如何正确地使用它。

一、什么是 HTTP_REFERER?

在深入了解 wp_get_referer() 之前,我们先简单回顾一下 HTTP_REFERER

HTTP_REFERER 是一个 HTTP 请求头,它包含了发起当前请求的页面的 URL。 简单来说,就是“你是从哪个页面跳转过来的?”。

举个例子:

假设你正在浏览我的博客 www.example.com,然后点击了一个链接,跳转到了 www.google.com。 那么,当你访问 www.google.com 时,你的浏览器会发送一个 HTTP 请求,其中就包含了 HTTP_REFERER,它的值会是 www.example.com

HTTP_REFERER 的用途:

  • 统计分析: 网站可以使用 HTTP_REFERER 来了解用户的来源,分析流量来源。
  • 防盗链: 网站可以根据 HTTP_REFERER 来判断请求是否来自自己的网站,防止他人盗用资源。
  • 用户体验: 某些情况下,可以根据 HTTP_REFERER 将用户重定向回之前的页面。

HTTP_REFERER 的局限性:

  • 并非总是存在: 用户可以通过设置浏览器或使用某些安全工具来阻止发送 HTTP_REFERER
  • 可能被篡改: 虽然篡改 HTTP_REFERER 比较麻烦,但理论上是可以实现的。
  • 隐私问题: HTTP_REFERER 可能会泄露用户的浏览历史,存在一定的隐私风险。

二、wp_get_referer() 的源码解析

好了,铺垫了这么多,终于要进入正题了。咱们来一起看看 wp_get_referer() 的源码,它位于 wp-includes/functions.php 文件中。为了方便阅读,我把代码简化并加上了注释:

<?php

/**
 * Retrieves the referring page URL.
 *
 * @since 2.0.4
 *
 * @return string|false The referer URL if set, otherwise false.
 */
function wp_get_referer() {
    $ref = '';

    if ( ! empty( $_SERVER['HTTP_REFERER'] ) ) {
        $ref = wp_unslash( $_SERVER['HTTP_REFERER'] );
    }

    /**
     * Filters the referer URL.
     *
     * @since 2.0.4
     *
     * @param string $ref The referer URL.
     */
    return apply_filters( 'wp_get_referer', $ref );
}

是不是很简单? 别被表象迷惑了,魔鬼往往藏在细节里。

  1. 获取 HTTP_REFERER

    $ref = ''; 首先,定义一个空字符串变量 $ref,用于存储 HTTP_REFERER 的值。

    if ( ! empty( $_SERVER['HTTP_REFERER'] ) ) { ... } 接着,判断 $_SERVER['HTTP_REFERER'] 是否为空。注意,这里使用的是 ! empty() 函数,它会检查变量是否存在且不为空。如果 HTTP_REFERER 存在且不为空,才会执行后面的代码。

    $ref = wp_unslash( $_SERVER['HTTP_REFERER'] ); 如果 HTTP_REFERER 存在,就使用 wp_unslash() 函数对其进行处理。wp_unslash() 函数的作用是移除字符串中的反斜杠。 为什么要移除反斜杠呢? 这是因为在某些情况下,服务器可能会自动在 HTTP_REFERER 中添加反斜杠,为了保证数据的准确性,我们需要将其移除。

  2. 应用过滤器:

    return apply_filters( 'wp_get_referer', $ref ); 最后,使用 apply_filters() 函数应用 wp_get_referer 过滤器。 apply_filters() 函数是 WordPress 的一个核心函数,它允许开发者通过过滤器来修改函数返回的值。 在这里,apply_filters( 'wp_get_referer', $ref ) 的作用是:将 $ref 的值传递给所有挂载在 wp_get_referer 过滤器上的函数,这些函数可以对 $ref 的值进行修改,最终 apply_filters() 函数会返回修改后的 $ref 值。

总结:

wp_get_referer() 函数的主要功能就是:

  • $_SERVER 数组中获取 HTTP_REFERER 的值。
  • 使用 wp_unslash() 函数移除反斜杠。
  • 使用 apply_filters() 函数应用 wp_get_referer 过滤器。

三、wp_safe_redirect()wp_validate_redirect():安全重定向

光有 wp_get_referer() 还不够, WordPress 还提供了两个相关的函数,用于安全地重定向用户:

  • wp_safe_redirect()
  • wp_validate_redirect()

这两个函数可以防止恶意用户通过篡改 HTTP_REFERER 或其他方式,将用户重定向到钓鱼网站或恶意网站。

  1. wp_validate_redirect():验证重定向 URL

    wp_validate_redirect() 函数的作用是验证一个 URL 是否安全,可以被用作重定向目标。它会检查 URL 是否在白名单中,或者是否是当前站点的 URL。

    <?php
    /**
     * Sanitizes a URL intended to be redirected to.
     *
     * Performs a number of checks for consistency.
     *
     * If the $good_protocol parameter is null, then the passed URL is checked
     * against the default safe protocols.
     *
     * @since 2.3.0
     *
     * @param string $url The URL to be validated.
     * @param string $default The URL to redirect to if the passed URL is invalid.
     * @param array|null $allowed_protocols Optional. Array of acceptable protocols.
     *                                      Defaults to those in wp_allowed_protocols().
     * @return string The sanitized URL.
     */
    function wp_validate_redirect( $url, $default = '' , $allowed_protocols = null ) {
        $url = wp_unslash( $url );
        if ( ! $url ) {
            return $default;
        }
    
        $url = str_replace( ' ', '%20', $url );
        $url = wp_kses_bad_protocol( $url, $allowed_protocols );
    
        // Prevent multiple slashes from breaking the redirect.
        $url = str_replace( '//', '/', $url );
    
        // Compare the normalized URL.
        if ( ! wp_http_validate_url( $url ) ) {
            return $default;
        }
    
        $url_parts = wp_parse_url( $url );
    
        // Prevent open redirects to arbitrary hosts.
        if ( ! empty( $url_parts['host'] ) ) {
            $home_url_parts = wp_parse_url( home_url() );
    
            if ( ! isset( $home_url_parts['host'] ) ) {
                return $default;
            }
    
            if ( strtolower( $url_parts['host'] ) !== strtolower( $home_url_parts['host'] ) ) {
                return $default;
            }
        }
    
        /**
         * Filters the safe redirect URL.
         *
         * @since 2.3.0
         *
         * @param string $url The redirect URL.
         * @param string $default The URL to redirect to if the passed URL is invalid.
         */
        return apply_filters( 'wp_safe_redirect_fallback', $url, $default );
    }

    参数说明:

    • $url:要验证的 URL。
    • $default:如果 URL 无效,则重定向到的默认 URL。
    • $allowed_protocols: 允许的协议列表, 默认为 wp_allowed_protocols() 返回的协议。

    工作流程:

    • 移除 URL 中的反斜杠,并进行URL编码
    • 使用 wp_kses_bad_protocol() 函数检查 URL 是否包含恶意协议(例如 javascript:)。
    • 使用 wp_http_validate_url() 函数验证 URL 的格式是否正确。
    • 检查 URL 的域名是否与当前站点的域名一致。
    • 应用 wp_safe_redirect_fallback 过滤器。
  2. wp_safe_redirect():安全重定向

    wp_safe_redirect() 函数的作用是安全地将用户重定向到指定的 URL。 它会先使用 wp_validate_redirect() 函数验证 URL 的安全性,然后再执行重定向。

    <?php
    /**
     * Redirects to another page.
     *
     * Performs a safe (local) redirect, using wp_redirect().
     *
     * @since 2.1.0
     *
     * @param string $location The path to redirect to.
     * @param int    $status   Optional. The HTTP response status code to use. Default 302 (Moved Temporarily).
     * @return bool False if $location is not a safe URL, otherwise true.
     */
    function wp_safe_redirect( $location, $status = 302 ) {
        $location = wp_validate_redirect( $location, home_url() );
    
        if ( ! $location ) {
            return false;
        }
    
        wp_redirect( $location, $status );
        return true;
    }

    参数说明:

    • $location:要重定向到的 URL。
    • $status:HTTP 状态码,默认为 302(临时重定向)。

    工作流程:

    • 使用 wp_validate_redirect() 函数验证 $location 的安全性。 如果 $location 无效,则使用 home_url() 作为默认的重定向 URL。
    • 如果 $location 有效,则使用 wp_redirect() 函数执行重定向。

四、使用示例

现在,我们来看几个使用 wp_get_referer()wp_validate_redirect()wp_safe_redirect() 的示例。

  1. 获取 HTTP_REFERER 并重定向回之前的页面:

    <?php
    $referer = wp_get_referer();
    
    if ( $referer ) {
        wp_safe_redirect( $referer );
        exit;
    } else {
        // 如果没有 HTTP_REFERER,则重定向到首页
        wp_safe_redirect( home_url() );
        exit;
    }

    在这个示例中,我们首先使用 wp_get_referer() 函数获取 HTTP_REFERER 的值。 如果 HTTP_REFERER 存在,则使用 wp_safe_redirect() 函数将用户重定向回之前的页面。 如果 HTTP_REFERER 不存在,则重定向到首页。

    注意: 在调用 wp_redirect()wp_safe_redirect() 函数后,一定要调用 exit() 函数,以防止后面的代码继续执行。

  2. 根据 HTTP_REFERER 显示不同的内容:

    <?php
    $referer = wp_get_referer();
    
    if ( strpos( $referer, 'www.example.com' ) !== false ) {
        // 如果用户来自 www.example.com,则显示特殊内容
        echo '<p>欢迎来自 example.com 的朋友!</p>';
    } else {
        // 否则,显示默认内容
        echo '<p>欢迎访问本站!</p>';
    }

    在这个示例中,我们根据 HTTP_REFERER 的值来判断用户的来源,并显示不同的内容。

  3. 自定义 wp_safe_redirect() 的行为:

    <?php
    add_filter( 'wp_safe_redirect_fallback', 'my_custom_redirect_fallback', 10, 2 );
    
    function my_custom_redirect_fallback( $url, $default ) {
        // 如果 URL 无效,则重定向到自定义的页面
        return home_url( '/my-custom-page/' );
    }

    在这个示例中,我们使用 wp_safe_redirect_fallback 过滤器来修改 wp_safe_redirect() 函数的行为。 如果 wp_validate_redirect() 函数验证 URL 无效,则会调用 my_custom_redirect_fallback() 函数,将用户重定向到自定义的页面。

五、注意事项

  • 不要过度依赖 HTTP_REFERER 由于 HTTP_REFERER 并非总是存在,并且可能被篡改,因此不要过度依赖它。 应该使用其他方式来验证用户的身份或来源。
  • 始终使用 wp_safe_redirect() 函数进行重定向: wp_safe_redirect() 函数可以防止恶意用户将用户重定向到钓鱼网站或恶意网站。
  • 注意隐私问题: HTTP_REFERER 可能会泄露用户的浏览历史,因此在使用时需要注意隐私问题。 可以考虑使用其他方式来跟踪用户的来源,例如使用 UTM 参数。
  • 理解 wp_validate_redirect() 函数的工作原理: wp_validate_redirect() 函数是保证重定向安全的关键。 了解它的工作原理,可以帮助你更好地保护你的网站和用户。

六、总结

wp_get_referer() 函数虽然简单,但却是 WordPress 安全体系中的重要一环。 通过它,我们可以获取 HTTP_REFERER,并使用 wp_validate_redirect()wp_safe_redirect() 函数进行安全重定向。

希望通过今天的讲解,大家能够更深入地理解 wp_get_referer() 函数的源码和使用方法,并在实际开发中正确地使用它,确保你的 WordPress 网站的安全。

今天的讲座就到这里,谢谢大家! 如果大家还有什么疑问,欢迎在评论区留言。

发表回复

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