WP Request Monitor & Blocker 监控WordPress HTTP 请求插件

2026年02月13日 | 分享 | 5条评论

闲着写了一个插件监控Wordpress HTTP 请求,保存以下代码为wp-request-monitor.php,放到wp-content/plugins/目录下即可

可以看插件或者主题等有没有偷偷摸摸的发送一些请求,泄露信息

<?php
/*
Plugin Name: WP Request Monitor & Blocker
Description: 监控 HTTP 请求(含耗时、Header、Body),支持正则拦截,过滤 Cron/Ajax,卸载时自动清理。
Version: 1.0
Author: Huilang
Author URI: https://huilang.me
*/

if (!defined('ABSPATH')) exit;

/**
 * 1. 数据库表初始化
 */
register_activation_hook(__FILE__, 'wrm_install');
function wrm_install() {
    global $wpdb;
    $table_name = $wpdb->prefix . 'wrm_logs';
    $charset_collate = $wpdb->get_charset_collate();

    $sql = "CREATE TABLE $table_name (
        id bigint(20) NOT NULL AUTO_INCREMENT,
        time datetime DEFAULT CURRENT_TIMESTAMP,
        duration float DEFAULT 0,
        method varchar(10),
        url text,
        request_headers longtext,
        request_body longtext,
        response_code varchar(50),
        response_headers longtext,
        response_body longtext,
        is_blocked tinyint(1) DEFAULT 0,
        PRIMARY KEY  (id)
    ) $charset_collate;";

    require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
    dbDelta($sql);
    add_option('wrm_blocked_rules', []);
}

/**
 * 2. 卸载清理逻辑
 */
register_uninstall_hook(__FILE__, 'wrm_uninstall');
function wrm_uninstall() {
    global $wpdb;
    $wpdb->query("DROP TABLE IF EXISTS " . $wpdb->prefix . 'wrm_logs');
    delete_option('wrm_blocked_rules');
}

/**
 * 3. 核心拦截与计时逻辑
 */
global $wrm_request_meta;
$wrm_request_meta = [];

add_filter('pre_http_request', 'wrm_core_interceptor', 1, 3);
function wrm_core_interceptor($pre, $args, $url) {
    global $wpdb, $wrm_request_meta;

    // 过滤高频无关请求
    if (isset($_POST['wrm_clear']) || isset($_POST['save_rules'])) return $pre;
    if (strpos($url, 'wp-cron.php') !== false || strpos($url, 'admin-ajax.php') !== false) return $pre;

    $request_id = md5($url . serialize($args));
    $wrm_request_meta[$request_id] = [
        'start_time' => microtime(true),
        'is_blocked' => false
    ];

    $rules = get_option('wrm_blocked_rules', []);
    foreach ($rules as $rule) {
        $rule = trim($rule);
        if (empty($rule)) continue;

        $is_hit = (strpos($rule, '/') === 0) ? @preg_match($rule, $url) : (strpos($url, $rule) !== false);

        if ($is_hit) {
            $wrm_request_meta[$request_id]['is_blocked'] = true;
            $wpdb->insert($wpdb->prefix . 'wrm_logs', [
                'time'            => current_time('mysql'),
                'duration'        => 0,
                'method'          => $args['method'],
                'url'             => $url,
                'request_headers' => maybe_serialize($args['headers']),
                'request_body'    => maybe_serialize($args['body']),
                'response_code'   => 'BLOCKED',
                'response_body'   => '该请求已被拦截规则匹配: ' . esc_html($rule),
                'is_blocked'      => 1
            ]);
            return new WP_Error('http_request_blocked', 'Monitor Blocked');
        }
    }
    return $pre;
}

/**
 * 4. 正常请求记录
 */
add_action('http_api_debug', 'wrm_core_logger', 10, 5);
function wrm_core_logger($response, $context, $class, $args, $url) {
    global $wpdb, $wrm_request_meta;

    $request_id = md5($url . serialize($args));
    if (!isset($wrm_request_meta[$request_id]) || $wrm_request_meta[$request_id]['is_blocked']) {
        unset($wrm_request_meta[$request_id]);
        return;
    }

    $duration = round(microtime(true) - $wrm_request_meta[$request_id]['start_time'], 4);
    unset($wrm_request_meta[$request_id]);

    $is_error = is_wp_error($response);
    $res_code = $is_error ? $response->get_error_code() : wp_remote_retrieve_response_code($response);
    $res_body = $is_error ? $response->get_error_message() : wp_remote_retrieve_body($response);
    $res_headers = $is_error ? [] : wp_remote_retrieve_headers($response);

    $wpdb->insert($wpdb->prefix . 'wrm_logs', [
        'time'             => current_time('mysql'),
        'duration'         => $duration,
        'method'           => $args['method'],
        'url'              => $url,
        'request_headers'  => maybe_serialize($args['headers']),
        'request_body'     => maybe_serialize($args['body']),
        'response_code'    => $res_code,
        'response_headers' => maybe_serialize($res_headers),
        'response_body'    => mb_strimwidth($res_body, 0, 10000, '... [Truncated]'),
        'is_blocked'       => 0
    ]);
}

/**
 * 5. 后台界面
 */
add_action('admin_menu', function() {
    add_menu_page('请求监控', '请求监控', 'manage_options', 'wrm', 'wrm_render', 'dashicons-rest-api');
});

function wrm_render() {
    global $wpdb;
    $table_name = $wpdb->prefix . 'wrm_logs';

    if (isset($_POST['save_rules']) && check_admin_referer('wrm_save_rules')) {
        $rules = explode("\n", str_replace("\r", "", $_POST['rules_text']));
        update_option('wrm_blocked_rules', array_filter(array_map('trim', $rules)));
        echo '<div class="updated"><p>拦截规则更新成功。</p></div>';
    }

    if (isset($_POST['wrm_clear']) && check_admin_referer('wrm_clear_logs')) {
        $wpdb->query("TRUNCATE TABLE $table_name");
        echo '<div class="updated"><p>日志已清空。</p></div>';
    }

    $per_page = 20;
    $current_page = isset($_GET['paged']) ? max(1, intval($_GET['paged'])) : 1;
    $offset = ($current_page - 1) * $per_page;
    $total_items = (int) $wpdb->get_var("SELECT COUNT(id) FROM $table_name");
    $total_pages = ceil($total_items / $per_page);
    $logs = $wpdb->get_results($wpdb->prepare("SELECT * FROM $table_name ORDER BY id DESC LIMIT %d OFFSET %d", $per_page, $offset));
    $rules_text = implode("\n", get_option('wrm_blocked_rules', []));
    ?>
    <style>
        .wrm-badge { padding: 2px 6px; border-radius: 3px; font-size: 11px; font-weight: bold; }
        .wrm-blocked { background: #333; color: #fff; }
        .wrm-ok { background: #00a32a; color: #fff; }
        .wrm-error { background: #d63638; color: #fff; }
        pre.code-view { background: #f6f7f7; padding: 10px; border: 1px solid #c3c4c7; overflow: auto; max-height: 150px; font-size: 12px; }
        .detail-box { padding:15px; background:#fff; border:1px solid #ccd0d4; margin-top: 5px; }
        .detail-label { display: block; margin: 10px 0 5px; font-weight: bold; color: #222; border-left: 4px solid #2271b1; padding-left: 8px; }
    </style>
    <div class="wrap">
        <h1>网络请求监控 & 拦截</h1>

        <div class="postbox" style="margin-top:20px; padding:15px;">
            <h3>拦截配置 (每行一个关键词或正则)</h3>
            <form method="post">
                <?php wp_nonce_field('wrm_save_rules'); ?>
                <textarea name="rules_text" style="width:100%; height:80px; font-family:monospace;" placeholder="example.com&#10;/^https:\/\/api\.*/i"><?php echo esc_textarea($rules_text); ?></textarea>
                <p><input type="submit" name="save_rules" class="button button-primary" value="保存拦截规则"></p>
            </form>
        </div>

        <div class="tablenav top">
            <div class="alignleft actions">
                <form method="post">
                    <?php wp_nonce_field('wrm_clear_logs'); ?>
                    <input type="submit" name="wrm_clear" class="button" value="清空日志 (<?php echo $total_items; ?>)">
                </form>
            </div>
            <div class="tablenav-pages">
                <?php echo paginate_links(['total' => $total_pages, 'current' => $current_page, 'base' => add_query_arg('paged', '%#%')]); ?>
            </div>
        </div>

        <table class="wp-list-table widefat fixed striped">
            <thead>
                <tr>
                    <th width="140">时间</th><th width="70">耗时</th><th width="80">状态</th><th width="70">方法</th><th>URL</th><th width="80">操作</th>
                </tr>
            </thead>
            <tbody>
                <?php foreach ($logs as $log): 
                    $status_class = $log->is_blocked ? 'wrm-blocked' : (intval($log->response_code) < 400 ? 'wrm-ok' : 'wrm-error');
                ?>
                <tr>
                    <td><?php echo $log->time; ?></td>
                    <td><?php echo $log->duration; ?>s</td>
                    <td><span class="wrm-badge <?php echo $status_class; ?>"><?php echo esc_html($log->response_code); ?></span></td>
                    <td><code><?php echo esc_html($log->method); ?></code></td>
                    <td style="word-break: break-all;"><?php echo esc_html($log->url); ?></td>
                    <td><button class="button button-small" onclick="jQuery('#details-<?php echo $log->id; ?>').toggle()">详情</button></td>
                </tr>
                <tr id="details-<?php echo $log->id; ?>" style="display:none;">
                    <td colspan="6">
                        <div class="detail-box">
                            <span class="detail-label">[Request Headers]</span>
                            <pre class="code-view"><?php print_r(maybe_unserialize($log->request_headers)); ?></pre>
                            
                            <span class="detail-label">[Request Body]</span>
                            <textarea style="width:100%; height:100px; font-family:monospace; background:#f9f9f9;" readonly><?php 
                                $req_body = maybe_unserialize($log->request_body);
                                echo esc_html(is_array($req_body) || is_object($req_body) ? json_encode($req_body, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) : $req_body); 
                            ?></textarea>

                            <span class="detail-label">[Response Body]</span>
                            <textarea style="width:100%; height:150px; font-family:monospace; background:#f9f9f9;" readonly><?php echo esc_html($log->response_body); ?></textarea>
                        </div>
                    </td>
                </tr>
                <?php endforeach; ?>
            </tbody>
        </table>
    </div>
    <?php
}


/**
 * 6. 在插件列表页添加“设置”链接
 */
add_filter('plugin_action_links_' . plugin_basename(__FILE__), 'wrm_plugin_action_links');

function wrm_plugin_action_links($links) {
    // 创建一个指向监控页面的链接
    $settings_link = '<a href="' . admin_url('admin.php?page=wrm') . '">' . __('Settings') . '</a>';
    
    // 将链接放入数组的最前面
    array_unshift($links, $settings_link);
    
    return $links;
}

5 条评论

发布评论