各位观众老爷,晚上好!今天咱们来聊聊 WordPress 里的一个“幕后英雄”:wp_get_referer_if_valid()
。别看它名字长,其实它干的活儿挺实在,专门负责验证 Referer
(来路)信息的真伪。这玩意儿在安全方面可有点作用,能帮咱们稍微挡一挡 CSRF 攻击啥的。
好了,废话不多说,咱们这就开始解剖一下这个函数的源码,看看它到底是怎么判断 Referer
有效性的。
一、Referer
是个啥?为啥要验证它?
在深入代码之前,先得弄清楚 Referer
是个什么东西。简单来说,Referer
是 HTTP 请求头中的一个字段,它告诉服务器,你是从哪个页面链接到当前页面的。
举个例子,你正在浏览 www.example.com/page1.html
,然后点击了一个链接,跳转到了 www.example.com/page2.html
。那么,当你访问 page2.html
时,你的浏览器会在 HTTP 请求头中带上 Referer: www.example.com/page1.html
这么一行信息。
那么,为啥要验证 Referer
呢?因为这玩意儿可以被伪造!恶意用户可以修改 HTTP 请求头,把 Referer
改成任何他们想要的值。这样一来,服务器就可能被欺骗,执行一些不应该执行的操作。
CSRF(Cross-Site Request Forgery,跨站请求伪造)攻击就经常利用这一点。攻击者会诱骗用户点击一个恶意链接,这个链接会向用户的网站发送一个请求,并且伪造 Referer
,让服务器误以为这个请求是用户自己发起的。
所以,验证 Referer
,虽然不能完全杜绝 CSRF 攻击,但至少可以提高安全性,增加攻击的难度。
二、wp_get_referer_if_valid()
源码解析
咱们直接上代码,然后逐行解释:
function wp_get_referer_if_valid() {
$ref = wp_get_raw_referer();
if ( ! $ref ) {
return false;
}
$val = wp_validate_redirect( $ref, false );
if ( ! $val ) {
return false;
}
return $val;
}
这段代码非常简洁,主要做了三件事:
- 获取原始
Referer
信息: 使用wp_get_raw_referer()
获取未经处理的Referer
值。 - 检查
Referer
是否为空: 如果Referer
为空,直接返回false
。 - 验证
Referer
的有效性: 使用wp_validate_redirect()
验证Referer
是否是合法的 URL。如果验证失败,返回false
。 - 返回验证后的
Referer
: 如果验证通过,返回验证后的Referer
值。
看起来很简单,是吧?但魔鬼藏在细节里。咱们一个个函数拆开来看。
2.1 wp_get_raw_referer()
:获取原始 Referer
这个函数负责从 $_SERVER
超全局变量中获取 HTTP_REFERER
的值。代码如下:
function wp_get_raw_referer() {
if ( ! isset( $_SERVER['HTTP_REFERER'] ) ) {
return '';
}
return trim( $_SERVER['HTTP_REFERER'] );
}
非常简单,就是从 $_SERVER
中取 HTTP_REFERER
字段,然后用 trim()
函数去除首尾的空白字符。 如果 HTTP_REFERER
不存在,则返回空字符串。
2.2 wp_validate_redirect()
:验证 Referer
的有效性
这是整个验证过程中最核心的函数,它负责判断 Referer
是否是一个合法的 URL,并且是否符合一些安全规则。
wp_validate_redirect()
函数的原型如下:
function wp_validate_redirect( $location, $default = '' ) {
$location = wp_sanitize_redirect( $location );
if ( ! $location ) {
return $default;
}
$location = wp_kses_bad_protocol( $location, array( 'http', 'https' ) );
if ( false !== strpos( $location, ';' ) ) {
return $default;
}
$parsed_url = wp_parse_url( $location );
if ( ! is_array( $parsed_url ) ) {
return $default;
}
// Absolute URL.
if ( isset( $parsed_url['host'] ) ) {
$allowed_hosts = apply_filters( 'allowed_redirect_hosts', array( wp_parse_url( home_url(), PHP_URL_HOST ) ) );
if ( ! in_array( strtolower( $parsed_url['host'] ), array_map( 'strtolower', $allowed_hosts ), true ) ) {
return $default;
}
} else {
$content_url = strtolower( str_replace( array( 'http://', 'https://' ), '', content_url() ) );
$site_url = strtolower( str_replace( array( 'http://', 'https://' ), '', site_url() ) );
$location_no_scheme = strtolower( str_replace( array( 'http://', 'https://' ), '', $location ) );
// If $location_no_scheme contains content_url or site_url, consider it safe.
if ( false !== strpos( $location_no_scheme, $content_url ) || false !== strpos( $location_no_scheme, $site_url ) ) {
return $location;
}
if ( preg_match( '#^//#', $location ) ) {
return $location;
}
}
return $location;
}
这段代码略微复杂,咱们一步一步来分析:
-
wp_sanitize_redirect()
:清理 URL首先,使用
wp_sanitize_redirect()
函数对 URL 进行清理。这个函数主要负责移除 URL 中的一些非法字符,例如换行符、回车符等等。function wp_sanitize_redirect( $location ) { $location = preg_replace( '|[^a-z0-9-%/_.~+?=&:[]@!$*'(),;]|i', '', $location ); $location = str_replace( ' ', '%20', $location ); return $location; }
这段代码使用正则表达式移除了 URL 中除了字母、数字、一些特殊字符之外的所有字符,然后将空格替换为
%20
。 -
检查 URL 是否为空:
如果清理后的 URL 为空,直接返回默认值
$default
。在wp_get_referer_if_valid()
中,$default
的值为false
。 -
wp_kses_bad_protocol()
:移除危险协议使用
wp_kses_bad_protocol()
函数移除 URL 中的危险协议,例如javascript:
、vbscript:
等等。这个函数可以防止 XSS 攻击。function wp_kses_bad_protocol( $string, $allowed_protocols ) { $string = wp_kses_no_null( $string ); wp_kses_allowed_protocols( $allowed_protocols ); $string = preg_replace_callback( '#^([srnt]*)([^:]+):#i', '_wp_kses_bad_protocol_once2', $string ); return $string; }
这个函数的核心是
_wp_kses_bad_protocol_once2()
,它使用正则表达式来查找 URL 中的协议,然后判断该协议是否在$allowed_protocols
列表中。如果不在列表中,就移除该协议。 在wp_validate_redirect()
中,$allowed_protocols
的值为array( 'http', 'https' )
,也就是说,只允许http
和https
协议。 -
检查 URL 中是否包含分号:
如果 URL 中包含分号
;
,直接返回默认值$default
。分号在 URL 中可能会导致安全问题,所以需要禁止。 -
wp_parse_url()
:解析 URL使用
wp_parse_url()
函数解析 URL,将 URL 分解成不同的部分,例如协议、主机名、路径等等。$parsed_url = wp_parse_url( $location );
如果解析失败,
wp_parse_url()
会返回false
。 如果返回值不是数组,直接返回默认值$default
。 -
检查主机名是否合法:
如果 URL 中包含主机名,需要检查主机名是否在允许的列表中。
if ( isset( $parsed_url['host'] ) ) { $allowed_hosts = apply_filters( 'allowed_redirect_hosts', array( wp_parse_url( home_url(), PHP_URL_HOST ) ) ); if ( ! in_array( strtolower( $parsed_url['host'] ), array_map( 'strtolower', $allowed_hosts ), true ) ) { return $default; } }
这段代码首先使用
apply_filters( 'allowed_redirect_hosts', ... )
获取允许的主机名列表。默认情况下,只允许当前网站的主机名。allowed_redirect_hosts
是一个过滤器,允许开发者自定义允许的主机名列表。然后,使用
in_array()
函数检查 URL 中的主机名是否在允许的列表中。如果不在列表中,直接返回默认值$default
。 -
处理相对URL:
如果没有设置host,表示是一个站内的相对URL,也需要验证
else { $content_url = strtolower( str_replace( array( 'http://', 'https://' ), '', content_url() ) ); $site_url = strtolower( str_replace( array( 'http://', 'https://' ), '', site_url() ) ); $location_no_scheme = strtolower( str_replace( array( 'http://', 'https://' ), '', $location ) ); // If $location_no_scheme contains content_url or site_url, consider it safe. if ( false !== strpos( $location_no_scheme, $content_url ) || false !== strpos( $location_no_scheme, $site_url ) ) { return $location; } if ( preg_match( '#^//#', $location ) ) { return $location; } }
首先获取
content_url
和site_url
去除协议头后的值,然后判断location
是否包含这些字符串,如果包含,则认为是安全的,直接返回。另外,如果
location
以//
开头,也认为是安全的,直接返回。 -
返回验证后的 URL:
如果所有的验证都通过了,说明 URL 是合法的,返回验证后的 URL。
三、wp_get_referer_if_valid()
的使用场景
wp_get_referer_if_valid()
函数主要用于验证 Referer
的有效性,防止 CSRF 攻击。它通常用于以下场景:
- 处理敏感操作: 例如修改用户资料、删除文章等等。在执行这些操作之前,可以先验证
Referer
,确保请求是从合法的页面发起的。 - 防止表单重复提交: 可以使用
Referer
来判断用户是否是从同一个表单页面提交的请求。
四、wp_get_referer_if_valid()
的局限性
虽然 wp_get_referer_if_valid()
可以提高安全性,但它并不能完全杜绝 CSRF 攻击。因为:
Referer
可以被伪造: 恶意用户可以修改 HTTP 请求头,伪造Referer
。Referer
可能会被浏览器禁用: 有些浏览器出于隐私保护的考虑,可能会禁用Referer
。
因此,不能完全依赖 wp_get_referer_if_valid()
来防止 CSRF 攻击,还需要结合其他的安全措施,例如:
- 使用 Nonce: 为每个表单生成一个唯一的 Nonce(一次性令牌),并在提交表单时验证 Nonce 的有效性。
- 使用 CAPTCHA: 验证用户是否是真人。
- 实施 SameSite Cookie 策略: 限制 Cookie 的跨站访问。
五、代码示例
下面是一个使用 wp_get_referer_if_valid()
函数的示例:
<?php
// 验证 Referer
$referer = wp_get_referer_if_valid();
if ( ! $referer ) {
// Referer 无效,拒绝请求
wp_die( 'Invalid Referer!' );
}
// Referer 有效,继续处理请求
// ...
?>
这段代码首先使用 wp_get_referer_if_valid()
函数验证 Referer
的有效性。如果 Referer
无效,使用 wp_die()
函数显示错误信息,并终止程序的执行。如果 Referer
有效,继续处理请求。
六、总结
wp_get_referer_if_valid()
函数是 WordPress 中一个用于验证 Referer
的函数。它可以帮助我们提高安全性,防止 CSRF 攻击。但它并不能完全杜绝 CSRF 攻击,还需要结合其他的安全措施。
总的来说,wp_get_referer_if_valid()
函数的验证流程可以总结为以下几点:
- 获取原始
Referer
信息。 - 清理 URL,移除非法字符。
- 移除危险协议。
- 检查 URL 中是否包含分号。
- 解析 URL。
- 检查主机名是否合法。
- 返回验证后的 URL。
希望今天的讲解对大家有所帮助。 记住,安全无小事,多一份防范,少一份风险! 下次再见!