解释 `wp_get_referer()` 函数的源码,它是如何获取 HTTP `Referer` 并进行安全检查的?

大家好!今天咱们来聊聊 WordPress 里一个看起来不起眼,但实际上挺重要的函数:wp_get_referer()

咱们的目标是:

  1. 深入浅出地剖析 wp_get_referer() 的源码。
  2. 搞清楚它是如何获取 HTTP Referer 的。
  3. 理解它做了哪些安全检查,以及为什么要这样做。

准备好了吗?咱们开始!

什么是 HTTP Referer

首先,我们需要理解什么是 HTTP Referer。简单来说,它是一个 HTTP 请求头,告诉服务器当前请求是从哪个页面链接过来的。比如,你正在浏览一个网站 A,然后点击了网站 A 上的一个链接,跳转到了网站 B。那么,当浏览器向网站 B 发送请求时,HTTP Referer 就会包含网站 A 的 URL。

HTTP Referer 可以用于很多用途,比如:

  • 统计网站的流量来源。
  • 防止图片盗链(防止其他网站直接引用你的图片)。
  • 增强安全性(例如,验证请求是否来自合法的页面)。

但是,需要注意的是,HTTP Referer 并非总是可靠的。用户可以通过浏览器设置或者某些工具来禁用或者修改 Referer

wp_get_referer() 源码剖析

现在,让我们来看看 wp_get_referer() 的源码。这个函数位于 wp-includes/functions.php 文件中。

function wp_get_referer() {
    /**
     * Filters the returned HTTP referer.
     *
     * @since 2.0.0
     *
     * @param string $ref Referer URL.
     */
    return apply_filters( 'wp_get_referer', wp_unslash( $_SERVER['HTTP_REFERER'] ?? '' ) );
}

是不是感觉非常简单? 别急,我们一点点分解。

  1. $_SERVER['HTTP_REFERER']: 这个是PHP超级全局变量 $_SERVER 的一个元素,它直接从 HTTP 请求头中获取 Referer 的值。如果请求头中没有 Referer,那么它将返回 null

  2. ?? '': 这是 PHP 7 新增的 null 合并运算符。它的作用是:如果 $_SERVER['HTTP_REFERER'] 的值为 null,则返回一个空字符串 ''。 这可以避免后续操作中出现 "trying to access array offset on null" 的错误。

  3. wp_unslash(): 这个函数的作用是移除反斜杠。 在某些服务器配置中,$_SERVER 数组中的值可能会被自动加上反斜杠进行转义。wp_unslash() 函数可以移除这些反斜杠,确保数据的原始性。 例如:

    $str = "It's a beautiful day.";
    $unslashed_str = wp_unslash( $str );
    echo $unslashed_str; // 输出:It's a beautiful day.
  4. apply_filters( 'wp_get_referer', ... ): 这是一个 WordPress 的钩子(hook)。apply_filters() 函数允许我们使用其他函数来修改 wp_get_referer() 的返回值。这为开发者提供了一种灵活的方式来自定义 Referer 的获取和处理逻辑。'wp_get_referer' 是这个 filter 的名称,允许其他插件或主题通过 add_filter() 添加自定义函数来修改 wp_get_referer() 的结果。

简而言之,wp_get_referer() 函数的作用就是:

  1. 尝试从 $_SERVER['HTTP_REFERER'] 中获取 Referer 的值。
  2. 如果 Referer 不存在,则返回空字符串。
  3. 移除可能存在的反斜杠。
  4. 允许通过 wp_get_referer 钩子修改返回值。

wp_safe_redirect()wp_validate_redirect() 中的安全检查

wp_get_referer() 本身并没有进行任何严格的安全检查。但是,它通常会与 wp_safe_redirect()wp_validate_redirect() 函数一起使用,以实现更安全的重定向。

wp_safe_redirect() 函数用于安全地将用户重定向到另一个页面。它会检查重定向的目标 URL 是否安全,以防止恶意重定向攻击。wp_validate_redirect() 函数是 wp_safe_redirect() 使用的辅助函数,用于验证 URL 的安全性。

让我们来看看 wp_safe_redirect() 的简化版代码:

function wp_safe_redirect( $location, $status = 302 ) {
    $location = wp_validate_redirect( $location, wp_get_referer() );

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

可以看到,wp_safe_redirect() 首先使用 wp_validate_redirect() 来验证 $location 的安全性,其中 wp_get_referer() 的返回值作为 wp_validate_redirect() 的第二个参数传入。

接下来,我们来看看 wp_validate_redirect() 函数的简化版代码:

function wp_validate_redirect( $location, $fallback = '' ) {
    $location = wp_sanitize_redirect( $location );

    // If протокол relative, make it absolute.
    if ( substr( $location, 0, 2 ) === '//' ) {
        $location = is_ssl() ? 'https:' . $location : 'http:' . $location;
    }

    // Allow only certain protocols.
    $allowed_protocols = wp_allowed_protocols();

    $location_scheme = wp_parse_url( $location, PHP_URL_SCHEME );

    if ( $location_scheme && ! in_array( $location_scheme, $allowed_protocols, true ) ) {
        return $fallback;
    }

    // Compare the hostnames of the redirect and the fallback.
    $orig_host = wp_parse_url( home_url(), PHP_URL_HOST );
    $redirect_host = wp_parse_url( $location, PHP_URL_HOST );

    if ( $orig_host && $redirect_host && $orig_host !== $redirect_host ) {
        return $fallback;
    }

    return $location;
}

让我们分解一下 wp_validate_redirect() 的工作流程:

  1. wp_sanitize_redirect( $location ): 这个函数用于清理 URL,移除一些潜在的危险字符。

  2. 协议相对 URL 处理: 如果 URL 以 // 开头,则根据当前是否使用 HTTPS 来添加 http:https: 前缀,将其转换为绝对 URL。

  3. wp_allowed_protocols(): 这个函数返回一个允许使用的协议列表,例如 http, https, mailto, ftp, ftps 等。wp_validate_redirect() 会检查 URL 的协议是否在允许列表中。

  4. 主机名比较: wp_validate_redirect() 会比较重定向 URL 的主机名和网站的主机名。如果它们不相同,则认为重定向是不安全的,并返回 $fallback URL。

wp_validate_redirect() 函数的第二个参数 $fallback,正是从 wp_get_referer() 获得的值。 也就是说,如果重定向的 URL 不安全,那么就会重定向到用户访问之前的页面。

为什么需要这些安全检查?

恶意重定向攻击是一种常见的网络攻击方式。攻击者会诱骗用户点击一个看似正常的链接,但实际上会将用户重定向到一个恶意网站,窃取用户的敏感信息,或者进行其他恶意行为。

通过使用 wp_safe_redirect()wp_validate_redirect() 函数,WordPress 可以有效地防止恶意重定向攻击。这些函数会检查重定向的 URL 是否安全,确保用户不会被重定向到恶意网站。

例如,假设一个攻击者试图创建一个恶意链接:

https://example.com/wp-login.php?redirect_to=http://evil.com

如果 WordPress 没有进行安全检查,那么用户在登录后就会被重定向到 http://evil.com 网站。攻击者可以在 evil.com 网站上伪造一个登录页面,诱骗用户输入用户名和密码,从而窃取用户的登录信息。

但是,如果使用了 wp_safe_redirect()wp_validate_redirect() 函数,那么 wp_validate_redirect() 就会检测到 http://evil.com 不是一个安全的 URL,并将其替换为 $fallback URL(也就是 wp_get_referer() 返回的值)。这样,用户就不会被重定向到恶意网站。

总结

wp_get_referer() 函数本身很简单,它的作用是获取 HTTP Referer 的值。但是,它通常会与 wp_safe_redirect()wp_validate_redirect() 函数一起使用,以实现更安全的重定向。

wp_validate_redirect() 函数会检查重定向的 URL 是否安全,防止恶意重定向攻击。它会比较重定向 URL 的主机名和网站的主机名,并检查 URL 的协议是否在允许列表中。如果重定向的 URL 不安全,那么就会重定向到 wp_get_referer() 返回的 $fallback URL,也就是用户访问之前的页面。

通过这些安全检查,WordPress 可以有效地保护用户免受恶意重定向攻击。

示例场景

为了更好地理解 wp_get_referer() 的作用,我们来看一个实际的例子。

假设你正在开发一个 WordPress 插件,该插件允许用户在登录后被重定向到他们访问之前的页面。你可以使用 wp_safe_redirect()wp_get_referer() 来实现这个功能。

<?php
/**
 * Plugin Name: Redirect After Login
 * Description: Redirect users to the page they were on before logging in.
 */

add_action( 'wp_login', 'redirect_after_login', 10, 2 );

function redirect_after_login( $user_login, $user ) {
    $redirect_to = wp_get_referer();

    if ( ! $redirect_to ) {
        // 如果没有 Referer,则重定向到管理后台
        $redirect_to = admin_url();
    }

    wp_safe_redirect( $redirect_to );
    exit;
}

在这个例子中,redirect_after_login() 函数会在用户登录后被调用。它首先使用 wp_get_referer() 获取用户访问之前的页面的 URL。如果 wp_get_referer() 返回空字符串,则重定向到管理后台。最后,它使用 wp_safe_redirect() 将用户重定向到 $redirect_to URL。

通过这个例子,我们可以看到 wp_get_referer() 在实际开发中的应用。它可以帮助我们获取用户的访问历史,并根据用户的访问历史来进行一些定制化的操作。

进一步思考

  • Referer 的局限性: 正如前面提到的,HTTP Referer 并非总是可靠的。用户可以通过浏览器设置或者某些工具来禁用或者修改 Referer。因此,在某些情况下,wp_get_referer() 可能会返回空字符串或者不正确的值。

  • CSRF 攻击: 虽然 wp_get_referer() 可以用于增强安全性,但它并不能完全防止 CSRF 攻击(跨站请求伪造)。CSRF 攻击是一种利用用户已登录的身份,在用户不知情的情况下,冒充用户执行操作的攻击方式。为了防止 CSRF 攻击,我们还需要使用其他的安全措施,例如 nonce(一次性随机数)。

  • 自定义 wp_get_referer 钩子: WordPress 提供了 wp_get_referer 钩子,允许我们自定义 wp_get_referer() 的返回值。这为开发者提供了一种灵活的方式来处理 Referer。例如,我们可以使用这个钩子来记录用户的访问历史,或者根据用户的访问历史来进行一些定制化的操作。

总结的总结

今天我们一起深入了解了 WordPress 的 wp_get_referer() 函数,包括它的源码、安全检查机制以及实际应用场景。希望通过这次的讲解,你对这个函数有了更清晰的认识。

记住,安全永远是第一位的!在开发 WordPress 插件或主题时,一定要注意安全问题,防止恶意攻击。

谢谢大家!希望下次有机会再和大家分享更多 WordPress 的技术知识。

发表回复

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