Init Sentinel – Bài 6: Xây dựng trang Admin theo dõi Security Logs đầy đủ

Widget dashboard chỉ dùng để quan sát nhanh. Khi cần điều tra thực sự, Init Sentinel cần một trang admin riêng nơi log có thể được lọc, sắp xếp, phân trang và xuất dữ liệu. Bài viết này hướng dẫn xây dựng trang Security Logs đầy đủ, từng bước một, trực tiếp từ code triển khai.

Init Sentinel – Bài 6: Xây dựng trang Admin theo dõi Security Logs đầy đủ

Sau bài này, Init Sentinel hoàn thiện vòng đời: ghi log → quan sát nhanh → điều tra chi tiết.

Bước 1: Giới hạn quyền truy cập trang Security Logs

Trang log bảo mật tuyệt đối không dành cho user thường. Ngay khi vào page callback, cần chặn toàn bộ user không đủ quyền.

if ( ! current_user_can('manage_options') ) {
    wp_die( esc_html__('You do not have permission to view this page.', 'init-html') );
}

Điều này đảm bảo chỉ Admin (hoặc role cao tương đương) mới có thể truy cập dữ liệu nhạy cảm.

Bước 2: Kiểm tra bảng log tồn tại trước khi query

Không giả định rằng bảng log luôn tồn tại. Trong lần kích hoạt đầu tiên hoặc khi module bị tắt, bảng có thể chưa được tạo.

$has_table = $wpdb->get_var( $wpdb->prepare(
    "SELECT COUNT(*) FROM information_schema.tables
     WHERE table_schema = DATABASE()
       AND table_name = %s",
    $table
) );

if ( ! $has_table ) {
    echo '<div class="wrap"><h1>Security Logs</h1><p>Security log table not found.</p></div>';
    return;
}

Cách làm này giúp wp-admin không bao giờ bị lỗi trắng chỉ vì thiếu bảng.

Bước 3: Thu thập và chuẩn hoá input từ URL

Trang log hỗ trợ nhiều bộ lọc phục vụ điều tra thực tế, mỗi input đều được ép kiểu và làm sạch ngay từ đầu.

$paged     = max(1, absint($_GET['paged'] ?? 1));
$per_page  = min( max(20, absint($_GET['per_page'] ?? 50)), 200 );
$q         = trim((string)($_GET['s'] ?? ''));
$code      = trim((string)($_GET['code'] ?? ''));
$endpoint  = trim((string)($_GET['endpoint'] ?? ''));
$action    = trim((string)($_GET['action_key'] ?? ''));
$user_id   = trim((string)($_GET['user'] ?? ''));
$ip        = trim((string)($_GET['ip'] ?? ''));
$date_from = trim((string)($_GET['from'] ?? ''));
$date_to   = trim((string)($_GET['to'] ?? ''));

Mỗi biến này tương ứng trực tiếp với một tiêu chí lọc trong bảng log.

Bước 4: Xây dựng WHERE clause theo từng điều kiện

Thay vì viết SQL cứng, Init Sentinel xây dựng mảng điều kiện và parameter theo input thực tế.

$where  = [];
$params = [];

if ($endpoint !== '') {
    $where[]  = 'endpoint LIKE %s';
    $params[] = $endpoint . '%';
}

if ($action !== '') {
    $where[]  = 'action LIKE %s';
    $params[] = '%' . $wpdb->esc_like($action) . '%';
}

if ($ip !== '') {
    $where[]  = 'ip_address LIKE %s';
    $params[] = '%' . $wpdb->esc_like($ip) . '%';
}

Cách này giúp query an toàn, dễ mở rộng và không bị rối khi thêm filter mới.

Bước 5: Lọc status code theo số hoặc theo nhóm

Status code có thể lọc chính xác hoặc theo nhóm như 4xx, 5xx để phục vụ điều tra nhanh.

if (preg_match('/^\d{3}$/', $code)) {
    $where[]  = 'status_code = %d';
    $params[] = (int)$code;
} elseif (preg_match('/^[245]xx$/', $code)) {
    $prefix = (int)$code[0];
    $where[]  = 'status_code BETWEEN %d AND %d';
    $params[] = $prefix * 100;
    $params[] = $prefix * 100 + 99;
}

Chỉ riêng đoạn này đã giúp tiết kiệm rất nhiều thời gian khi soi log 403.

Bước 6: Đếm tổng số kết quả để phân trang

Trước khi query dữ liệu, cần biết tổng số dòng để tính số trang.

$total = (int) $wpdb->get_var(
    $wpdb->prepare("SELECT COUNT(*) FROM {$table} {$where_sql}", ...$params)
);

Query đếm tách riêng giúp logic rõ ràng và dễ debug.

Bước 7: Query dữ liệu theo trang hiện tại

Dữ liệu log được lấy với LIMIT và OFFSET, đảm bảo mỗi request luôn nhẹ.

$sql = "SELECT id,created_at,user_id,ip_address,endpoint,action,status_code,user_agent
        FROM {$table}
        {$where_sql}
        ORDER BY {$orderby} {$order}
        LIMIT %d OFFSET %d";

$rows = $wpdb->get_results(
    $wpdb->prepare($sql, ...array_merge($params, [$per_page, $offset]))
);

orderby luôn bị giới hạn whitelist để tránh SQL injection.

Bước 8: Hiển thị thời gian theo timezone site

Log được lưu bằng UTC, nhưng hiển thị theo timezone WordPress để admin dễ đọc.

$time_local = get_date_from_gmt(
    gmdate('Y-m-d H:i:s', strtotime($r->created_at)),
    get_option('date_format').' '.get_option('time_format')
);

Dữ liệu gốc không bao giờ bị biến đổi.

Bước 9: Thêm chức năng export CSV

Trang log hỗ trợ xuất CSV phục vụ điều tra offline hoặc chia sẻ cho đội kỹ thuật.

if ( isset($_GET['export']) && $_GET['export'] === 'csv' ) {
    check_admin_referer('init_html_sentinel_export');
    header('Content-Type: text/csv; charset=utf-8');
    header('Content-Disposition: attachment; filename=security-logs.csv');
}

Export bị giới hạn số dòng và bắt buộc nonce để tránh lạm dụng.

Kết quả cuối cùng

Trang Security Logs này là trung tâm điều tra của Init Sentinel. Mọi hành vi bị chặn đều có thể được lọc, soi và truy vết tại đây.

Đây là mảnh ghép cuối cùng hoàn thiện Init Sentinel như một hệ thống theo dõi truy cập trái phép đúng nghĩa trong WordPress.

Lý tưởng nhất là trang này luôn trống. Nếu không, tức là có kẻ đang lục lọi hệ thống.

Bình luận


  • Không có bình luận.

Init Toolbox

Nhấn Ctrl + \ trên máy tính, hoặc vuốt sang trái ở bất kỳ đâu trên mobile.

Đăng nhập





Đang tải...