各位程序猿、程序媛们,晚上好!今天咱们来聊聊 WordPress 里一个挺有意思的小家伙:wp_get_referer_if_valid()
。别看它名字长,其实干的活儿挺实在,就是负责验证 HTTP_REFERER
这个东西靠不靠谱。
好,废话不多说,直接开讲!
啥是 HTTP_REFERER
?
在Web世界里,HTTP_REFERER
是一个HTTP请求头,它告诉服务器,用户是从哪个页面链接过来的。 简单来说,就是“我是谁,我从哪儿来”。
举个栗子:你正在浏览“我的博客”,然后点击了一个链接跳转到“你的博客”,这时,浏览器发给“你的博客”服务器的HTTP请求里,HTTP_REFERER
的值就是“我的博客”的URL。
为什么要验证 HTTP_REFERER
?
HTTP_REFERER
虽然方便,但有个致命的问题:它很容易被伪造! 坏人可以轻松地修改这个值,冒充是从一个“安全”的页面跳转过来的,以此来绕过一些安全检查,或者进行一些恶意操作,比如跨站请求伪造 (CSRF) 攻击。
所以,验证 HTTP_REFERER
就显得很重要了,它可以帮助我们判断请求的来源是否可信,从而提高网站的安全性。
wp_get_referer_if_valid()
是干啥的?
wp_get_referer_if_valid()
函数就是 WordPress 用来验证 HTTP_REFERER
的。 它会检查 HTTP_REFERER
是否存在,是否符合一定的规则,如果验证通过,就返回 HTTP_REFERER
的值;否则,就返回 false
。
wp_get_referer_if_valid()
源码剖析
让我们深入 wp-includes/pluggable.php
(通常在这个文件里) 看看 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;
}
是不是很简单? 只有三行代码!
$ref = wp_get_raw_referer();
: 首先,它调用了wp_get_raw_referer()
函数来获取原始的HTTP_REFERER
值。if ( $ref && wp_validate_redirect( $ref, false ) ) { ... }
: 然后,它判断$ref
是否存在,并且使用wp_validate_redirect()
函数来验证$ref
是否是一个有效的 URL。return $ref;
: 如果验证通过,就返回$ref
的值。return false;
: 否则,就返回false
。
可以看到,wp_get_referer_if_valid()
函数的关键在于 wp_get_raw_referer()
和 wp_validate_redirect()
这两个函数。 接下来,咱们就分别分析一下它们。
wp_get_raw_referer()
:获取原始的 HTTP_REFERER
wp_get_raw_referer()
函数的作用很简单,就是从 $_SERVER
超全局变量中获取 HTTP_REFERER
的值。 源码如下:
function wp_get_raw_referer() {
if ( ! empty( $_SERVER['HTTP_REFERER'] ) ) {
return wp_unslash( $_SERVER['HTTP_REFERER'] );
}
return '';
}
if ( ! empty( $_SERVER['HTTP_REFERER'] ) ) { ... }
: 首先,它判断$_SERVER['HTTP_REFERER']
是否存在且不为空。return wp_unslash( $_SERVER['HTTP_REFERER'] );
: 如果存在,就使用wp_unslash()
函数来移除反斜杠,并返回HTTP_REFERER
的值。return '';
: 否则,返回空字符串。
wp_unslash()
函数的作用是移除字符串中的反斜杠。 为什么要移除反斜杠呢? 因为有些服务器会自动在 HTTP_REFERER
的值中添加反斜杠,为了保证 HTTP_REFERER
的值是原始的,我们需要移除这些反斜杠。
wp_validate_redirect()
:验证 URL 是否有效
wp_validate_redirect()
函数才是真正验证 HTTP_REFERER
是否有效的关键。 它的作用是检查一个 URL 是否是一个有效的重定向目标。换句话说,就是判断这个 URL 是否可以安全地用于重定向。
wp_validate_redirect()
函数的源码比较长,咱们一点一点来分析:
function wp_validate_redirect( $url, $allowed_hosts = '' ) {
$original_url = $url;
$url = wp_sanitize_redirect( $url );
if ( ! $url ) {
return false;
}
if ( wp_is_url_in_content( $url ) ) {
return $url;
}
$home_url = wp_parse_url( home_url() );
if ( ! $home_url ) {
return false;
}
// If the URL is a relative path, then it's fine.
if ( strpos( $url, '://' ) === false ) {
return $url;
}
$parsed_url = wp_parse_url( $url );
if ( ! is_array( $parsed_url ) ) {
return false;
}
// Scheme check.
if ( ! isset( $parsed_url['scheme'] ) || ! in_array( $parsed_url['scheme'], array( 'http', 'https', 'feed' ), true ) ) {
return false;
}
// Host check.
if ( isset( $parsed_url['host'] ) ) {
$home_host = $home_url['host'];
$is_safe = strtolower( $parsed_url['host'] ) === strtolower( $home_host );
if ( ! $is_safe && ! empty( $allowed_hosts ) ) {
$allowed_hosts = array_map( 'strtolower', (array) $allowed_hosts );
$is_safe = in_array( strtolower( $parsed_url['host'] ), $allowed_hosts, true );
}
if ( ! $is_safe ) {
return false;
}
}
/**
* Filters a validated redirect URL.
*
* @since 4.5.0
*
* @param string $url The validated redirect URL.
* @param string $original_url The URL prior to validation.
*/
return apply_filters( 'wp_safe_redirect_url', $url, $original_url );
}
咱们把这段代码分成几个部分来分析:
$original_url = $url;
: 保存原始的 URL,以便在过滤器中使用。$url = wp_sanitize_redirect( $url );
: 使用wp_sanitize_redirect()
函数对 URL 进行清理。if ( ! $url ) { return false; }
: 如果清理后的 URL 为空,说明 URL 无效,返回false
。if ( wp_is_url_in_content( $url ) ) { return $url; }
: 检查 URL 是否在内容中,如果是,则直接返回 URL。 这个函数主要是为了兼容一些特殊情况,比如 URL 包含特殊字符等。$home_url = wp_parse_url( home_url() );
: 获取网站的 URL,并使用wp_parse_url()
函数解析 URL。if ( ! $home_url ) { return false; }
: 如果解析网站 URL 失败,返回false
。if ( strpos( $url, '://' ) === false ) { return $url; }
: 如果 URL 是一个相对路径,说明 URL 是站内链接,直接返回 URL。$parsed_url = wp_parse_url( $url );
: 使用wp_parse_url()
函数解析 URL。if ( ! is_array( $parsed_url ) ) { return false; }
: 如果解析 URL 失败,返回false
。if ( ! isset( $parsed_url['scheme'] ) || ! in_array( $parsed_url['scheme'], array( 'http', 'https', 'feed' ), true ) ) { return false; }
: 检查 URL 的协议是否是http
、https
或feed
,如果不是,返回false
。if ( isset( $parsed_url['host'] ) ) { ... }
: 检查 URL 的主机名是否是网站的主机名,或者是允许的主机名。return apply_filters( 'wp_safe_redirect_url', $url, $original_url );
: 使用wp_safe_redirect_url
过滤器,允许开发者自定义验证规则。
可以看到,wp_validate_redirect()
函数的验证逻辑还是比较复杂的,它主要做了以下几件事情:
- 清理 URL: 使用
wp_sanitize_redirect()
函数对 URL 进行清理,移除一些不安全的字符。 - 检查协议: 检查 URL 的协议是否是
http
、https
或feed
。 - 检查主机名: 检查 URL 的主机名是否是网站的主机名,或者是允许的主机名。
- 允许自定义验证规则: 使用
wp_safe_redirect_url
过滤器,允许开发者自定义验证规则。
wp_sanitize_redirect()
:清理 URL
wp_sanitize_redirect()
函数的作用是清理 URL,移除一些不安全的字符。 源码如下:
function wp_sanitize_redirect( $location ) {
$location = preg_replace( '|[^a-z0-9-~+_.?#=&;,/:%@$|*'"![]{}`´()\x80-xff]|i', '', $location );
$location = wp_kses_no_null( $location );
// Remove remaining invalid lower ASCII chars.
$location = preg_replace( '/[x00-x1f]/', '', $location );
$location = str_replace( '%0d', '', $location );
return $location;
}
这个函数主要做了以下几件事情:
- *`$location = pregreplace( ‘|[^a-z0-9-~+.?#=&;,/:%@$|‘"![]{}
´()\x80-xff]|i', '', $location );
**: 使用正则表达式移除 URL 中不合法的字符。 $location = wp_kses_no_null( $location );
: 移除 URL 中的 NULL 字符。$location = preg_replace( '/[x00-x1f]/', '', $location );
: 移除 URL 中剩余的无效的 ASCII 字符。$location = str_replace( '%0d', '', $location );
: 移除 URL 中的%0d
字符。
wp_is_url_in_content()
: 检查 URL 是否在内容中
这个函数相对简单,主要检测是否是 data:
URI 或者包含特殊字符。
function wp_is_url_in_content( $url ) {
$url = strtolower( $url );
if ( strpos( $url, 'data:' ) === 0 ) {
return true;
}
// Check for unicode chars.
if ( preg_match( '/%u[0-9a-f]{2,4}|&#x[0-9a-f]{1,6};|&#[0-9]{1,7};/', $url ) ) {
return true;
}
return false;
}
允许的主机名 ($allowed_hosts
)
在 wp_validate_redirect()
函数中,还有一个 $allowed_hosts
参数,用于指定允许的主机名。 如果 $allowed_hosts
参数为空,那么只允许网站的主机名。 如果 $allowed_hosts
参数不为空,那么允许网站的主机名和 $allowed_hosts
参数指定的主机名。
wp_get_referer_if_valid()
的应用场景
wp_get_referer_if_valid()
函数在 WordPress 中有很多应用场景,比如:
- 防止 CSRF 攻击: 在处理一些敏感操作时,可以使用
wp_get_referer_if_valid()
函数来验证请求的来源是否可信,从而防止 CSRF 攻击。 - 统计页面访问量: 可以使用
wp_get_referer_if_valid()
函数来获取用户的来源页面,从而统计页面的访问量。 - 个性化用户体验: 可以使用
wp_get_referer_if_valid()
函数来获取用户的来源页面,从而为用户提供个性化的用户体验。
使用示例
下面是一个使用 wp_get_referer_if_valid()
函数的示例:
$referer = wp_get_referer_if_valid();
if ( $referer ) {
// Referer 有效
echo 'Referer: ' . esc_url( $referer );
} else {
// Referer 无效
echo 'Referer 无效';
}
在这个示例中,我们首先调用 wp_get_referer_if_valid()
函数来获取 HTTP_REFERER
的值。 如果 HTTP_REFERER
的值有效,就输出 HTTP_REFERER
的值;否则,就输出 "Referer 无效"。 注意使用了 esc_url()
函数来对输出的 URL 进行转义,防止 XSS 攻击。
总结
wp_get_referer_if_valid()
函数是 WordPress 中一个用于验证 HTTP_REFERER
的函数。 它通过 wp_get_raw_referer()
函数获取原始的 HTTP_REFERER
值,然后使用 wp_validate_redirect()
函数来验证 HTTP_REFERER
是否是一个有效的 URL。wp_validate_redirect()
还会调用 wp_sanitize_redirect()
来清理 URL。
wp_get_referer_if_valid()
函数在 WordPress 中有很多应用场景,可以用于防止 CSRF 攻击、统计页面访问量、个性化用户体验等。
总的来说,wp_get_referer_if_valid()
函数是一个很有用的函数,可以帮助我们提高 WordPress 网站的安全性。
深入思考
HTTP_REFERER
始终是不可靠的,即使通过了wp_get_referer_if_valid()
的验证。 攻击者仍然可以通过多种方式伪造HTTP_REFERER
。 因此,不应该仅仅依赖HTTP_REFERER
来做安全验证。- CSRF Token 是更可靠的 CSRF 防御机制。 应该尽可能使用 CSRF Token 来保护敏感操作。
wp_validate_redirect()
函数不仅仅用于验证HTTP_REFERER
,还可以用于验证其他的 URL。
好啦,今天的讲座就到这里。 希望大家对 wp_get_referer_if_valid()
函数有了更深入的了解。 如果有什么问题,欢迎随时提问! 祝大家编程愉快!