无插件限制WordPress搜索频率图文教程 保护服务器免受恶意刷搜攻击

无插件限制WordPress搜索频率图文教程 保护服务器免受恶意刷搜攻击

AI 智能摘要
为了有效解决这一问题,我们可以为WordPress的搜索功能添加一个频率限制机制。以下代码提供了一个完善的解决方案,它允许您设置在特定时间窗口内允许的最大搜索次数,并在超过限制时自动封禁该IP一段时间。这里主题铺分享一下无插件限制WordPress搜索频率图文教程,保护服务器免受恶意刷搜攻击。

WordPress的搜索功能虽然方便用户查找内容,但如果被恶意用户或机器人频繁刷搜,可能会导致服务器CPU使用率和负载状态瞬间飙升至100%爆满,严重影响网站的正常运行和用户体验。尤其是在共享主机环境下,这种资源耗尽的情况更为常见。

为了有效解决这一问题,我们可以为WordPress的搜索功能添加一个频率限制机制。以下代码提供了一个完善的解决方案,它允许您设置在特定时间窗口内允许的最大搜索次数,并在超过限制时自动封禁该IP一段时间。这里主题铺分享一下无插件限制WordPress搜索频率图文教程,保护服务器免受恶意刷搜攻击。

函数功能说明

这段PHP代码通过WordPress的pre_get_posts钩子来拦截搜索请求,并利用WordPress的缓存机制(wp_cache)来记录用户的搜索行为和封禁状态。

主要功能点:

  • 自定义配置: 您可以根据网站的实际需求,灵活调整以下参数:
    • $search_limit_count:在指定时间窗口内,允许的最大搜索次数。
    • $time_window_in_minutes:时间窗口的长度,以分钟为单位(您可以修改代码将其改为秒)。
    • $block_time_in_seconds:当用户超过搜索限制后,该IP将被封禁的时间,以秒为单位。
    • $block_message:当IP被封禁时,向用户显示的提示信息。
  • IP识别: 准确获取访问者的IP地址,支持通过HTTP_CLIENT_IPHTTP_X_FORWARDED_FORREMOTE_ADDR获取,以应对CDN或代理服务。
  • 搜索请求拦截: 在执行搜索查询之前,检查访问IP是否已被封禁或是否达到搜索频率限制。
  • 友好的封禁提示: 如果IP被封禁,会向用户显示一个包含剩余封禁时间的提示页面(HTTP状态码429 Too Many Requests),告知其稍后再试。
  • 全站提示(可选): 即使在非搜索页面,如果IP仍处于封禁状态,也会在页面顶部显示一个轻量级的提示条,告知用户其搜索功能受限。
  • 缓存机制: 利用wp_cache_setwp_cache_get来存储IP的搜索记录和封禁状态,高效且不依赖数据库,减少服务器负担。

如何将代码部署到WordPress子比主题(或其他WordPress主题)

将以下PHP代码添加到您的WordPress主题的functions.php文件中。如果您使用的是WordPress子比主题,通常建议将其添加到子主题的functions.php中,或者WordPress子比主题提供的func.php文件中,以避免主题更新时代码被覆盖。

<?php
// 主题铺提醒:如果您的主题已定义了<?php标签,请勿重复添加

class WXS_SearchFrequencyLimit {
    // 配置参数可根据需要修改
    private $search_limit_count = 3; // 允许的最大搜索次数
    private $time_window_in_minutes = 1; // 时间窗口(分钟),可根据需求修改为秒
    private $block_time_in_seconds = 60; // 超过限制后封禁时间(秒),建议与时间窗口时长一致
    private $block_message = '您的搜索频率过高,请在 %d 秒后再试。'; // 封禁提示消息

    public function __construct() {
        // 注册钩子,在WordPress加载主查询之前检查搜索频率
        add_filter( 'pre_get_posts', array( $this, 'check_search_limit' ) );

        // 如果不是在后台,则在任何页面加载时检查IP的封禁状态
        if ( ! is_admin() ) {
            add_action( 'wp_loaded', array( $this, 'check_block_status_on_any_page' ) );
        }
    }

    /**
     * 获取访问者的IP地址
     * @return string 访问者IP
     */
    private function get_visitor_ip() {
        if ( ! empty( $_SERVER['HTTP_CLIENT_IP'] ) ) {
            $ip = $_SERVER['HTTP_CLIENT_IP'];
        } elseif ( ! empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
            // 处理HTTP_X_FORWARDED_FOR可能包含多个IP的情况
            $ip = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'])[0];
        } else {
            $ip = $_SERVER['REMOTE_ADDR'];
        }
        return sanitize_text_field( $ip );
    }

    /**
     * 设置IP的封禁缓存
     * @param string $key 缓存键
     * @param int $expiration 缓存过期时间(秒)
     */
    private function set_block_cache($key, $expiration) {
        $expire_time = time() + $expiration;
        // 使用'search_limit'组,方便管理和清除
        wp_cache_set($key, $expire_time, 'search_limit', $expiration);
    }

    /**
     * 获取IP剩余的封禁时间
     * @param string $key 缓存键
     * @return int 剩余封禁时间(秒),如果未被封禁则返回0
     */
    private function get_remaining_block_time($key) {
        $expire_time = wp_cache_get($key, 'search_limit');
        if ($expire_time === false) {
            return 0; // 未被封禁
        }
        $remaining = $expire_time - time();
        return $remaining > 0 ? $remaining : 0; // 确保返回非负数
    }

    /**
     * 在非搜索页面检查IP的封禁状态并显示提示
     */
    public function check_block_status_on_any_page() {
        $visitor_ip = $this->get_visitor_ip();
        $cache_key = 'search_block_' . $visitor_ip;
        $remaining_time = $this->get_remaining_block_time($cache_key);

        // 如果IP仍在封禁期内且当前页面不是搜索结果页,则显示提示
        if ($remaining_time > 0 && !is_search()) {
            $message = sprintf($this->block_message, $remaining_time);
            // 使用wp_body_open钩子在<body>标签之后插入提示
            add_action('wp_body_open', function() use ($message) {
                echo '<div style="background-color: #fff3cd; color: #856404; padding: 15px; text-align: center; border: 1px solid #ffeeba; margin-bottom: 20px;">';
                echo esc_html($message); // 安全输出提示消息
                echo '</div>';
            });
        }
    }

    /**
     * 检查搜索频率限制并处理
     * @param WP_Query $query WordPress查询对象
     * @return WP_Query
     */
    public function check_search_limit($query) {
        // 确保只在前端、主查询且是搜索请求时执行
        if (!is_admin() && $query->is_search && $query->is_main_query()) {
            $visitor_ip = $this->get_visitor_ip();
            $limit = $this->search_limit_count;
            $window_seconds = $this->time_window_in_minutes * 60; // 将分钟转换为秒
            $block_time = $this->block_time_in_seconds;

            $cache_key = 'search_block_' . $visitor_ip; // IP封禁状态的缓存键
            $records_key = 'search_records_' . $visitor_ip; // IP搜索记录的缓存键

            // 1. 检查IP是否正在被封禁
            $remaining_time = $this->get_remaining_block_time($cache_key);
            if ($remaining_time > 0) {
                wp_die(
                    sprintf($this->block_message, $remaining_time),
                    '搜索频率限制', // 页面标题
                    array('response' => 429) // HTTP 429 Too Many Requests
                );
            }

            // 2. 获取IP的搜索记录
            $search_records = wp_cache_get($records_key, 'search_limit', false, $found);
            if (!$found) {
                $search_records = array(); // 如果没有找到记录,则初始化为空数组
            }
            $current_time = time();

            // 3. 过滤掉时间窗口外的旧记录
            $search_records = array_filter($search_records, function($timestamp) use ($current_time, $window_seconds) {
                return ($current_time - $timestamp) < $window_seconds;
            });

            // 4. 检查是否达到搜索频率限制
            if (count($search_records) >= $limit) {
                // 达到限制,封禁IP
                $this->set_block_cache($cache_key, $block_time);
                // 清除旧的搜索记录,避免重复累积
                wp_cache_delete($records_key, 'search_limit'); 

                wp_die(
                    sprintf($this->block_message, $block_time),
                    '搜索频率限制',
                    array('response' => 429)
                );
            } else {
                // 未达到限制,添加当前搜索时间戳到记录
                $search_records[] = $current_time;
                // 更新缓存,设置缓存过期时间与时间窗口一致,确保记录自动清理
                wp_cache_set($records_key, $search_records, 'search_limit', $window_seconds);
            }
        }

        return $query; // 返回修改后的查询对象
    }
}

// 实例化类以激活功能
new WXS_SearchFrequencyLimit();
?>

配置参数说明(可自行修改):

  • private $search_limit_count = 3;
    • 含义:$time_window_in_minutes设定的时间内,允许同一个IP进行的最大搜索次数。
    • 建议: 根据网站流量和用户行为习惯调整。对于一般博客,3-5次可能比较合理。
  • private $time_window_in_minutes = 1;
    • 含义: 统计搜索次数的时间窗口长度,以分钟为单位。
    • 建议: 1-5分钟是常见的选择。如果您希望更严格,可以设置为1分钟。
  • private $block_time_in_seconds = 60;
    • 含义: 当IP达到搜索次数限制后,将被封禁的时间,以秒为单位。
    • 建议: 为了用户体验和有效拦截,通常建议将封禁时间设置得与时间窗口长度一致或略长,例如这里设置为60秒(1分钟)。
  • private $block_message = '您的搜索频率过高,请在 %d 秒后再试。';
    • 含义: 当IP被封禁时,向用户显示的提示信息。%d会自动替换为剩余的封禁秒数。

最后总结

这段代码提供了一个非常实用的WordPress搜索功能保护机制。通过限制搜索频率,它能有效防止恶意刷搜行为,从而避免服务器CPU使用率和负载状态的异常升高。对于任何运行WordPress网站的管理员来说,尤其是在服务器资源有限的共享主机环境下,实施这样的防护措施是至关重要且高效的。这段代码的模块化设计和对WordPress缓存的利用,使其成为一个优雅而强大的解决方案。

© 版权声明
THE END
喜欢就支持一下吧
点赞14 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容