分析 WordPress `wp_get_referer_if_valid()` 函数的源码:如何验证 `HTTP_REFERER` 的有效性。

各位观众老爷们,大家好! 今天咱们来聊聊WordPress里一个看似不起眼,但其实挺重要的函数:wp_get_referer_if_valid()。 别看名字长,作用可不小,它主要负责验证 HTTP_REFERER 这个“引荐来源网址”的有效性。 嘿,这玩意儿可不是你想随便伪造就能糊弄过去的! WordPress可是有自己的一套验证逻辑的。

啥是 HTTP_REFERER? 为什么要验证它?

简单来说,HTTP_REFERER 就是你从哪个网页链接跳转过来的。 比如说,你从Google搜索结果点进了一个WordPress博客,那么这个博客收到请求时,HTTP_REFERER 就会包含Google搜索结果页面的网址。

那为啥要验证它呢? 因为 HTTP_REFERER 这玩意儿是可以伪造的! 坏人可以伪造一个假的 HTTP_REFERER,冒充某个网站,以此来做一些坏事,比如:

  • CSRF攻击(跨站请求伪造): 伪造 HTTP_REFERER 诱导用户在不知情的情况下执行一些操作。
  • 垃圾评论: 伪造 HTTP_REFERER 绕过某些评论系统的验证机制,发布垃圾评论。
  • 统计欺诈: 伪造 HTTP_REFERER 虚增网站的访问量。

所以,验证 HTTP_REFERER 的有效性,可以提高网站的安全性,防止一些恶意行为。

wp_get_referer_if_valid() 源码剖析

咱们直接上代码,边看边聊:

function wp_get_referer_if_valid() {
    $ref = wp_get_raw_referer();

    if ( $ref && wp_validate_redirect( $ref, false ) ) {
        return $ref;
    }

    return false;
}

这段代码看着挺简洁的,主要做了两件事:

  1. 获取原始 HTTP_REFERER 调用 wp_get_raw_referer() 函数获取未经处理的 HTTP_REFERER
  2. 验证 HTTP_REFERER 的有效性: 调用 wp_validate_redirect() 函数验证 HTTP_REFERER 是否是一个有效的重定向目标。

如果 HTTP_REFERER 存在且通过了验证,就返回它;否则,返回 false

wp_get_raw_referer(): 拿到最原始的 HTTP_REFERER

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

    return $ref;
}

这个函数非常简单粗暴,就是直接从 $_SERVER['HTTP_REFERER'] 里面取出 HTTP_REFERER 的值。

  • $_SERVER['HTTP_REFERER']: PHP 超全局变量,包含了客户端发送的 HTTP_REFERER 信息。
  • wp_unslash(): WordPress 的一个函数,用于移除字符串中由 magic_quotes_gpc 添加的反斜杠。
  • trim(): PHP函数,用于移除字符串首尾的空白字符。

wp_validate_redirect(): 验证重定向目标,才是重头戏!

这才是整个验证过程的核心! 咱们来看看 wp_validate_redirect() 的源码(简化版,去掉了部分不常用的参数处理):

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

    if ( ! $location ) {
        return $default;
    }

    $home_url = parse_url( home_url() );

    if ( ! is_array( $home_url ) ) {
        return $default;
    }

    $valid = true;

    try {
        $parsed_url = wp_parse_url( $location );
    } catch ( Exception $e ) {
        return $default;
    }

    if ( ! is_array( $parsed_url ) ) {
        return $default;
    }

    // Absolute URL.
    if ( isset( $parsed_url['host'] ) ) {

        // Compare the hostnames.
        if ( strtolower( $parsed_url['host'] ) !== strtolower( $home_url['host'] ) ) {
            $valid = false;
        }

        // Compare the schemes.
        if ( isset( $parsed_url['scheme'] ) && isset( $home_url['scheme'] ) && strtolower( $parsed_url['scheme'] ) !== strtolower( $home_url['scheme'] ) ) {
            $valid = false;
        }
    }

    /**
     * Filters whether the redirect location is valid.
     *
     * @since 2.3.0
     *
     * @param bool   $valid    Whether the redirect location is valid.
     * @param string $location The redirect location.
     * @param string $default  The fallback redirect location.
     */
    $valid = apply_filters( 'wp_validate_redirect', $valid, $location, $default );

    if ( $valid ) {
        return $location;
    }

    return $default;
}

咱们一步一步来解读这段代码:

  1. wp_sanitize_redirect(): 清理重定向 URL

    $location = wp_sanitize_redirect( $location );

    首先,使用 wp_sanitize_redirect() 函数对 URL 进行清理。 这个函数会移除 URL 中一些不安全的字符,比如换行符、回车符等,防止 URL 注入攻击。

  2. 检查 URL 是否为空

    if ( ! $location ) {
        return $default;
    }

    如果清理后的 URL 为空,说明原始的 HTTP_REFERER 可能包含一些恶意字符,直接返回默认值。

  3. 解析站点 URL

    $home_url = parse_url( home_url() );
    
    if ( ! is_array( $home_url ) ) {
        return $default;
    }

    使用 parse_url() 函数解析 WordPress 站点的 URL,获取主机名、协议等信息。 home_url() 函数返回的是站点首页的 URL。 如果解析失败,说明站点 URL 配置有问题,返回默认值。

  4. 解析 HTTP_REFERER URL

    try {
        $parsed_url = wp_parse_url( $location );
    } catch ( Exception $e ) {
        return $default;
    }
    
    if ( ! is_array( $parsed_url ) ) {
        return $default;
    }

    同样使用 parse_url() 函数解析 HTTP_REFERER 的 URL。 如果解析失败,说明 HTTP_REFERER 不是一个有效的 URL,返回默认值。

  5. 比较主机名和协议

    // Absolute URL.
    if ( isset( $parsed_url['host'] ) ) {
    
        // Compare the hostnames.
        if ( strtolower( $parsed_url['host'] ) !== strtolower( $home_url['host'] ) ) {
            $valid = false;
        }
    
        // Compare the schemes.
        if ( isset( $parsed_url['scheme'] ) && isset( $home_url['scheme'] ) && strtolower( $parsed_url['scheme'] ) !== strtolower( $home_url['scheme'] ) ) {
            $valid = false;
        }
    }

    这是验证的核心部分。 如果 HTTP_REFERER 是一个绝对 URL(包含主机名),则比较它的主机名和协议是否与站点 URL 的主机名和协议一致。 如果不一致,则认为 HTTP_REFERER 无效。

    注意: 这里只比较主机名和协议,不比较路径。 也就是说,只要 HTTP_REFERER 的主机名和协议与站点 URL 一致,即使路径不同,也认为它是有效的。

  6. 过滤器 wp_validate_redirect

    $valid = apply_filters( 'wp_validate_redirect', $valid, $location, $default );

    WordPress 提供了一个过滤器 wp_validate_redirect,允许开发者自定义验证逻辑。 通过这个过滤器,你可以添加自己的验证规则,比如检查 HTTP_REFERER 是否在白名单中,或者检查 HTTP_REFERER 是否包含特定的参数。

  7. 返回结果

    if ( $valid ) {
        return $location;
    }
    
    return $default;

    如果 HTTP_REFERER 通过了所有验证,就返回它;否则,返回默认值。

总结: wp_validate_redirect() 的验证逻辑

咱们用一个表格来总结一下 wp_validate_redirect() 的验证逻辑:

步骤 描述
1. 清理 URL 使用 wp_sanitize_redirect() 函数移除 URL 中不安全的字符。
2. 检查 URL 是否为空 如果 URL 为空,返回默认值。
3. 解析站点 URL 使用 parse_url() 函数解析站点 URL,获取主机名和协议。
4. 解析 HTTP_REFERER URL 使用 parse_url() 函数解析 HTTP_REFERER 的 URL。
5. 比较主机名和协议 如果 HTTP_REFERER 是一个绝对 URL,则比较它的主机名和协议是否与站点 URL 的主机名和协议一致。 如果不一致,则认为 HTTP_REFERER 无效。
6. 应用过滤器 应用 wp_validate_redirect 过滤器,允许开发者自定义验证逻辑。
7. 返回结果 如果 HTTP_REFERER 通过了所有验证,就返回它;否则,返回默认值。

wp_sanitize_redirect(): 清理 URL 的小能手

咱们再简单看看 wp_sanitize_redirect() 函数的源码:

function wp_sanitize_redirect( $location ) {
    $location = preg_replace( '|[^a-z0-9-~+_.?#=&;,/:%!@*'()[]\x80-\xff]|i', '', $location );
    $location = wp_kses_no_null( $location );

    // Remove any remaining invalid protocol relative URLs.
    $location = _wp_http_validate_url( $location );

    return $location;
}

这个函数主要做了三件事:

  1. 移除不安全的字符: 使用正则表达式移除 URL 中除了字母、数字、特殊字符以外的所有字符。
  2. 移除 NULL 字符: 使用 wp_kses_no_null() 函数移除 URL 中的 NULL 字符,防止 NULL 字符截断 URL。
  3. 验证 URL 的协议: 使用 _wp_http_validate_url() 函数验证 URL 的协议是否安全。

wp_kses_no_null(): 移除 NULL 字符

function wp_kses_no_null( $string ) {
    $string = preg_replace( '/+/', '', $string );
    $string = str_replace( array( '%00', "x0" ), '', $string );

    return $string;
}

这个函数非常简单,就是使用正则表达式和字符串替换函数移除字符串中的 NULL 字符。

_wp_http_validate_url(): 验证 URL 协议

function _wp_http_validate_url( $url ) {
    $allowed_protocols = wp_allowed_protocols();
    $url               = trim( $url );
    if ( empty( $url ) ) {
        return $url;
    }
    if ( strpos( $url, ':' ) === false ) {
        return $url;
    }
    $url_parts = explode( ':', $url, 2 );
    $scheme    = trim( $url_parts[0] );

    if ( in_array( strtolower( $scheme ), $allowed_protocols, true ) ) {
        return $url;
    }

    return '';
}

这个函数主要用于验证 URL 的协议是否在白名单中。

  • wp_allowed_protocols(): 获取允许的协议列表, 默认情况下,允许的协议包括 http, https, ftp, ftps, mailto, news, irc, gopher, nntp, telnet, mms, rtsp, svn, tel, fax, xmpp, webcal, webcal+.
  • 如果 URL 的协议不在白名单中,则返回空字符串。

wp_get_referer_if_valid() 的应用场景

了解了 wp_get_referer_if_valid() 函数的原理,咱们来看看它在 WordPress 中有哪些应用场景:

  • 重定向: 在用户登录、注册、找回密码等操作后,可以使用 wp_get_referer_if_valid() 函数获取有效的 HTTP_REFERER,然后将用户重定向到该页面。

    $redirect_to = wp_get_referer_if_valid();
    if ( ! $redirect_to ) {
        $redirect_to = home_url();
    }
    wp_safe_redirect( $redirect_to );
    exit;
  • 评论: 在评论系统中,可以使用 wp_get_referer_if_valid() 函数验证 HTTP_REFERER,防止垃圾评论。

  • 表单提交: 在表单提交处理程序中,可以使用 wp_get_referer_if_valid() 函数验证 HTTP_REFERER,防止 CSRF 攻击。

总结

wp_get_referer_if_valid() 函数是 WordPress 中一个重要的安全函数,用于验证 HTTP_REFERER 的有效性。 它通过一系列的验证步骤,包括清理 URL、比较主机名和协议、应用过滤器等,确保 HTTP_REFERER 是一个有效的重定向目标。 了解 wp_get_referer_if_valid() 函数的原理,可以帮助我们更好地理解 WordPress 的安全机制,并编写更安全的代码。

好了,今天的讲座就到这里。 感谢各位观众老爷的观看! 希望大家有所收获!

发表回复

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