阐述 WordPress `wp_get_referer()` 函数的源码:如何获取 `HTTP_REFERER` 并进行安全检查。

各位代码界的泥石流们,晚上好!今天咱们来聊聊WordPress里一个看似不起眼,实则暗藏玄机的函数——wp_get_referer()。别看它名字平平无奇,但在保护你的网站免受CSRF攻击方面,它可是个小能手。 准备好,咱们要开始解剖这个函数了!

开场白:HTTP_REFERER 是个什么鬼?

在开始之前,我们需要先了解一下HTTP_REFERER 这个家伙。简单来说,HTTP_REFERER 是 HTTP 请求头中的一个字段,它告诉服务器,你是从哪个页面链接过来的。比如说,你从Google搜索点击了一个链接进入我的网站,那么你的浏览器就会在请求头里带上 HTTP_REFERER: https://www.google.com/

但是!注意这个“但是”,HTTP_REFERER 这玩意儿并不可靠。为什么呢?因为它完全由客户端控制,浏览器想填什么就填什么,甚至可以不填。这就给了一些不法分子可乘之机。

wp_get_referer():守卫者的职责

wp_get_referer() 函数的主要任务就是获取 HTTP_REFERER,并进行一些基本的安全检查,以确保来源的可靠性,防止CSRF(Cross-Site Request Forgery,跨站请求伪造)攻击。

源码解剖:一步一步来

让我们一起来看看 wp-includes/functions.php 文件中 wp_get_referer() 的源码(截至WordPress 6.4.3):

function wp_get_referer() {
    /**
     * Filters the referer URL.
     *
     * @since 2.0.0
     *
     * @param string $ref URL of the referer.
     */
    $ref = apply_filters( 'wp_get_referer', isset( $_SERVER['HTTP_REFERER'] ) ? wp_unslash( $_SERVER['HTTP_REFERER'] ) : '' );

    return $ref;
}

是不是感觉非常简单?别急,好戏还在后头。我们来逐行分析:

  1. function wp_get_referer() {: 定义一个名为 wp_get_referer 的函数。

  2. `/ … */**: 这是函数的文档注释,描述了函数的作用和用法。其中最重要的是@param string $ref URL of the referer.,它说明了这个函数返回的是referer的URL,并且可以通过wp_get_referer` 过滤器进行修改。

  3. $ref = apply_filters( 'wp_get_referer', isset( $_SERVER['HTTP_REFERER'] ) ? wp_unslash( $_SERVER['HTTP_REFERER'] ) : '' );: 这一行是整个函数的灵魂所在。我们把它拆开来看:

    • isset( $_SERVER['HTTP_REFERER'] ) ? wp_unslash( $_SERVER['HTTP_REFERER'] ) : '': 这是一个三元运算符,意思是:
      • 如果 $_SERVER['HTTP_REFERER'] 存在(也就是说,请求头里有 HTTP_REFERER 字段),那么就执行 wp_unslash( $_SERVER['HTTP_REFERER'] )
      • 否则,就返回一个空字符串 ''
    • wp_unslash( $_SERVER['HTTP_REFERER'] ): 这个函数的作用是移除斜杠转义。在某些服务器配置下,PHP会自动对 $_GET$_POST$_COOKIE 等全局变量进行斜杠转义。wp_unslash() 可以将这些转义的斜杠移除,保证数据的原始性。
    • apply_filters( 'wp_get_referer', ... ): 这是 WordPress 的过滤器钩子。它允许其他插件或主题修改 wp_get_referer() 函数的返回值。第一个参数 'wp_get_referer' 是过滤器的名称,第二个参数是默认的返回值(也就是上面三元运算符的结果)。
  4. return $ref;: 函数返回处理后的 HTTP_REFERER 值。

安全性考量:为什么需要这个函数?

你可能会问,直接用 $_SERVER['HTTP_REFERER'] 不行吗?为什么还要绕这么个弯子?

原因很简单:安全!

  • 防止 XSS 攻击: 虽然 wp_get_referer() 本身并没有做非常严格的 XSS 过滤,但是 wp_unslash() 至少可以防止一些简单的 XSS 攻击。而且,通过过滤器钩子 wp_get_referer,我们可以添加更严格的 XSS 过滤逻辑。
  • 为 CSRF 防御提供基础: wp_get_referer() 只是 CSRF 防御的第一步。有了这个函数,我们就可以获取到请求的来源,然后与我们期望的来源进行比较,判断请求是否合法。

代码示例:如何使用 wp_get_referer()

下面是一个简单的例子,演示了如何使用 wp_get_referer() 来检查请求的来源是否是本站:

<?php
/**
 *  检查请求来源是否是本站
 */
function is_referer_from_this_site() {
    $referer = wp_get_referer();

    // 如果没有 referer,直接返回 false
    if ( empty( $referer ) ) {
        return false;
    }

    // 获取本站的域名
    $home_url = home_url();

    // 检查 referer 是否以本站域名开头
    if ( strpos( $referer, $home_url ) === 0 ) {
        return true; // 来自本站
    } else {
        return false; // 来自其他站点
    }
}

// 使用示例
if ( is_referer_from_this_site() ) {
    // 来自本站的请求,可以安全地处理
    echo '请求来自本站,继续处理...';
} else {
    // 来自其他站点的请求,可能是 CSRF 攻击
    echo '警告:请求来自其他站点,可能存在安全风险!';
    // 可以选择拒绝处理该请求,或者进行更严格的验证
}
?>

进阶用法:利用过滤器进行自定义处理

wp_get_referer() 函数的强大之处在于它的过滤器钩子 wp_get_referer。你可以使用这个钩子来修改 wp_get_referer() 的返回值,添加自定义的逻辑。

例如,你可以使用这个钩子来:

  • 添加更严格的 XSS 过滤: 使用 wp_kses() 函数对 HTTP_REFERER 进行过滤,移除所有不安全的 HTML 标签和属性。
  • 检查 referer 是否在白名单中: 维护一个允许的 referer 列表,只允许来自这些 referer 的请求。
  • 记录 referer 信息: 将 referer 信息记录到数据库中,用于分析用户来源。

下面是一个使用过滤器钩子的示例,演示了如何添加更严格的 XSS 过滤:

<?php
/**
 *  使用 wp_kses() 对 HTTP_REFERER 进行过滤
 */
function my_wp_get_referer_filter( $referer ) {
    // 定义允许的 HTML 标签和属性
    $allowed_html = array(
        'a' => array(
            'href' => array(),
            'title' => array(),
        ),
        'b' => array(),
        'strong' => array(),
        'i' => array(),
        'em' => array(),
    );

    // 使用 wp_kses() 进行过滤
    $filtered_referer = wp_kses( $referer, $allowed_html );

    return $filtered_referer;
}

// 添加过滤器
add_filter( 'wp_get_referer', 'my_wp_get_referer_filter' );
?>

防御 CSRF 攻击:更进一步

仅仅依靠 wp_get_referer() 是不够的,要彻底防御 CSRF 攻击,还需要采取其他措施,例如:

  • 使用 Nonce: Nonce(Number used once)是一个一次性的随机数,可以用来验证请求的合法性。WordPress 提供了 wp_nonce_field()wp_create_nonce()wp_verify_nonce() 等函数来方便地生成和验证 Nonce。
  • 验证 HTTP 请求方法: 对于修改数据的操作,应该只允许使用 POST 请求。
  • 使用 SameSite Cookie: 设置 Cookie 的 SameSite 属性为 StrictLax,可以防止跨站请求携带 Cookie。

总结:wp_get_referer() 的价值

wp_get_referer() 函数虽然简单,但它在 WordPress 的安全体系中扮演着重要的角色。它提供了一个获取和处理 HTTP_REFERER 的标准方式,并且允许开发者通过过滤器钩子进行自定义处理,为 CSRF 防御提供了基础。

一些建议:

  • 永远不要完全信任 HTTP_REFERER
  • 结合其他安全措施,例如 Nonce、HTTP 请求方法验证、SameSite Cookie 等,来彻底防御 CSRF 攻击。
  • 仔细审查所有来自外部的数据,包括 HTTP_REFERER,防止 XSS 攻击。
  • 定期更新 WordPress 和插件,确保使用的是最新的安全版本。

最后:

希望今天的讲解能帮助你更好地理解 wp_get_referer() 函数,并在你的 WordPress 项目中安全地使用它。记住,安全无小事,每一个细节都可能影响你的网站安全。

下次再见,祝各位代码路上,bug 少一点,头发多一点!

发表回复

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