2026年5月20日

2026年5月20日

WordPressのアクティビティログを設定する方法

はじめに

アクティビティログ(監査ログ)は、WordPressサイトで行われた操作(ログイン・投稿変更・設定変更・プラグイン操作等)を記録します。不正アクセスの検出・コンプライアンス要件の充足・問題発生時の原因究明に不可欠です。

症状・原因

  • 誰かが投稿を変更したが誰の操作かわからない
  • 不正ログインが疑われるがアクセスログだけでは詳細が不明
  • 管理者複数人でサイトを運営しており操作履歴の追跡が必要
  • コンプライアンス要件でユーザー操作の記録が義務付けられている

解決手順

ステップ1:WP Activity Log プラグインを設定する

# WP Activity Log をインストール・有効化
wp plugin install wp-security-audit-log --activate

# プラグインの設定(WP-CLI経由)
wp option update wsal_pruning_date "6 months"  # ログを6ヶ月保持

# アクティビティログの確認
wp eval "
if (function_exists('wsal_freemius')) {
    echo 'WP Activity Log is active' . PHP_EOL;
}
"
// WP Activity Log が記録するイベント(主要なもの):
// - ユーザーのログイン・ログアウト・ログイン失敗
// - 投稿・ページの作成・編集・削除・公開
// - ユーザーアカウントの作成・変更・削除
// - プラグイン・テーマのインストール・有効化・無効化・削除
// - WordPress 設定・パーマリンク・メニューの変更
// - メディアファイルのアップロード・削除
// - コメントの承認・スパム・削除
// - ウィジェット・カスタマイザーの変更

ステップ2:カスタムアクティビティログを実装する

// functions.php: 独自のアクティビティログシステム

// ① ログテーブルを作成(プラグイン有効化時)
function create_activity_log_table(): void {
    global $wpdb;
    $table_name = $wpdb->prefix . 'activity_log';
    $charset_collate = $wpdb->get_charset_collate();

    $sql = "CREATE TABLE IF NOT EXISTS $table_name (
        id          bigint(20) unsigned NOT NULL AUTO_INCREMENT,
        user_id     bigint(20) unsigned NOT NULL DEFAULT 0,
        action      varchar(100) NOT NULL,
        object_type varchar(50)  NOT NULL,
        object_id   bigint(20)   NOT NULL DEFAULT 0,
        message     text         NOT NULL,
        ip_address  varchar(45)  NOT NULL,
        created_at  datetime     NOT NULL DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (id),
        KEY user_id (user_id),
        KEY created_at (created_at)
    ) $charset_collate;";

    require_once ABSPATH . 'wp-admin/includes/upgrade.php';
    dbDelta($sql);
}
add_action('after_switch_theme', 'create_activity_log_table');

// ② ログを記録する共通関数
function log_activity(string $action, string $object_type, int $object_id, string $message): void {
    global $wpdb;
    $wpdb->insert(
        $wpdb->prefix . 'activity_log',
        [
            'user_id'     => get_current_user_id(),
            'action'      => sanitize_key($action),
            'object_type' => sanitize_key($object_type),
            'object_id'   => $object_id,
            'message'     => sanitize_textarea_field($message),
            'ip_address'  => $_SERVER['REMOTE_ADDR'] ?? '',
            'created_at'  => current_time('mysql'),
        ],
        ['%d', '%s', '%s', '%d', '%s', '%s', '%s']
    );
}

ステップ3:重要な操作をログに記録するフックを設定する

// functions.php: 各操作をログに記録

// ① ログイン成功・失敗を記録
add_action('wp_login', function(string $user_login, \WP_User $user): void {
    log_activity('login_success', 'user', $user->ID, "ログイン成功: {$user_login}");
}, 10, 2);

add_action('wp_login_failed', function(string $username): void {
    global $wpdb;
    $wpdb->insert($wpdb->prefix . 'activity_log', [
        'user_id'     => 0,
        'action'      => 'login_failed',
        'object_type' => 'user',
        'object_id'   => 0,
        'message'     => "ログイン失敗: {$username}",
        'ip_address'  => $_SERVER['REMOTE_ADDR'] ?? '',
        'created_at'  => current_time('mysql'),
    ], ['%d', '%s', '%s', '%d', '%s', '%s', '%s']);
});

// ② 投稿の変更を記録
add_action('post_updated', function(int $post_id, \WP_Post $after, \WP_Post $before): void {
    if ($before->post_status !== $after->post_status) {
        log_activity('post_status_changed', 'post', $post_id,
            "投稿ステータス変更: {$before->post_status} → {$after->post_status} ({$after->post_title})");
    }
    if ($before->post_content !== $after->post_content) {
        log_activity('post_updated', 'post', $post_id, "投稿を更新: {$after->post_title}");
    }
}, 10, 3);

// ③ プラグインの有効化・無効化を記録
add_action('activated_plugin', function(string $plugin): void {
    log_activity('plugin_activated', 'plugin', 0, "プラグイン有効化: {$plugin}");
});

add_action('deactivated_plugin', function(string $plugin): void {
    log_activity('plugin_deactivated', 'plugin', 0, "プラグイン無効化: {$plugin}");
});

// ④ ユーザー権限変更を記録
add_action('set_user_role', function(int $user_id, string $role, array $old_roles): void {
    $user = get_userdata($user_id);
    $old_role = implode(', ', $old_roles);
    log_activity('user_role_changed', 'user', $user_id,
        "ユーザー権限変更: {$user->user_login} {$old_role} → {$role}");
}, 10, 3);

ステップ4:ログを管理画面で確認できるようにする

// functions.php: アクティビティログの管理画面ページ

add_action('admin_menu', function(): void {
    add_menu_page(
        'アクティビティログ', 'アクティビティログ',
        'manage_options', 'activity-log',
        'render_activity_log_page',
        'dashicons-list-view', 80
    );
});

function render_activity_log_page(): void {
    global $wpdb;
    $table = $wpdb->prefix . 'activity_log';
    $logs = $wpdb->get_results(
        "SELECT l.*, u.user_login FROM {$table} l
         LEFT JOIN {$wpdb->users} u ON l.user_id = u.ID
         ORDER BY l.created_at DESC LIMIT 100"
    );

    echo '<div class="wrap"><h1>アクティビティログ</h1>';
    echo '<table class="widefat striped"><thead><tr>';
    echo '<th>日時</th><th>ユーザー</th><th>操作</th><th>詳細</th><th>IP</th>';
    echo '</tr></thead><tbody>';

    foreach ($logs as $log) {
        $user = $log->user_login ?: '未ログイン';
        echo '<tr>';
        echo '<td>' . esc_html($log->created_at) . '</td>';
        echo '<td>' . esc_html($user) . '</td>';
        echo '<td>' . esc_html($log->action) . '</td>';
        echo '<td>' . esc_html($log->message) . '</td>';
        echo '<td>' . esc_html($log->ip_address) . '</td>';
        echo '</tr>';
    }
    echo '</tbody></table></div>';
}

ステップ5:不審な操作を検出してアラートを送る

// functions.php: 不審な操作を検出

add_action('activity_logged', function(string $action, int $user_id, string $ip): void {
    // 深夜の管理者操作を通知
    $hour = (int) current_time('G');
    if (in_array($action, ['plugin_activated', 'user_role_changed', 'option_updated'], true)
        && ($hour < 5 || $hour > 23)) {
        wp_mail(
            get_option('admin_email'),
            "[警告] 深夜の管理操作: {$action}",
            "IP: {$ip}\nユーザーID: {$user_id}\n時刻: " . current_time('Y-m-d H:i:s')
        );
    }
}, 10, 3);
# ログをエクスポート(定期バックアップ用)
wp eval "
global \$wpdb;
\$logs = \$wpdb->get_results(\"SELECT * FROM {\$wpdb->prefix}activity_log ORDER BY created_at DESC\", ARRAY_A);
\$csv = array_map('str_getcsv', array_map('array_values', \$logs));
echo implode(PHP_EOL, array_map(fn(\$row) => implode(',', \$row), \$csv));
" > /var/backups/wordpress/activity_log_$(date +%Y%m%d).csv

注意事項

  • アクティビティログはデータベースに蓄積されるためストレージを消費します。定期的に古いログを削除するか、外部ストレージに保存する仕組みを設けてください。WP Activity Logプラグインの「Pruning」設定で自動削除期間を設定できます
  • ログに記録するIPアドレスはCloudflare等のプロキシを経由する場合はHTTP_CF_CONNECTING_IPHTTP_X_FORWARDED_FORヘッダーから取得する必要があります
  • ログテーブルには個人情報(IPアドレス・ユーザー名)が含まれます。プライバシーポリシーへの記載とGDPR等の規制への対応を確認してください

まとめ

アクティビティログの設定は①wp plugin install wp-security-audit-logでプラグインを導入、②独自実装の場合はactivity_logテーブルを作成して共通log_activity()関数を定義、③wp_loginpost_updatedactivated_pluginset_user_roleフックで操作を記録、④管理画面ページで直近100件を一覧表示、⑤深夜の管理操作を検出してメール通知します。

お気軽にご相談ください

お見積りへ お問い合わせへ