2026年5月19日

2026年5月19日

WordPressにWAF(Webアプリケーションファイアウォール)を設定する方法

はじめに

WAF(Web Application Firewall)は不正なリクエストをブロックするセキュリティ機能です。SQLインジェクション・XSS・ブルートフォース攻撃などのWebアプリへの攻撃から保護します。WordPressにはプラグイン・Cloudflare・サーバーレベルの3段階で設定できます。

症状・原因

  • アクセスログに不審なURLパラメーター(?id=1' OR '1'='1など)が記録されている
  • wp-login.phpへのアクセスが毎分数十件以上ある
  • 知らない管理者ユーザーが追加されている
  • Googleから「このサイトはハッキングされている可能性があります」と警告が出る

解決手順

ステップ1:Nginxレベルでリクエストを制限する

# /etc/nginx/conf.d/wordpress-security.conf

# ログインページへのリクエストレート制限
limit_req_zone $binary_remote_addr zone=wp_login:10m rate=5r/m;
limit_req_zone $binary_remote_addr zone=wp_api:10m   rate=30r/m;

server {
    # wp-login.phpのレート制限(1分間に5回まで)
    location = /wp-login.php {
        limit_req zone=wp_login burst=3 nodelay;
        limit_req_status 429;

        # 管理者IPのみ許可する場合
        # allow 203.0.113.1;
        # deny all;

        fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
        include fastcgi_params;
    }

    # REST APIのレート制限
    location /wp-json/ {
        limit_req zone=wp_api burst=10 nodelay;
        limit_req_status 429;
        try_files $uri $uri/ /index.php$is_args$args;
    }

    # xmlrpc.phpをブロック(使用しない場合)
    location = /xmlrpc.php {
        deny all;
        return 444;
    }

    # 悪意のあるクエリをブロック
    location ~* "(eval\(|base64_decode|gzinflate|str_rot13)" {
        deny all;
    }

    # 隠しファイルへのアクセスをブロック
    location ~ /\. {
        deny all;
    }
}

ステップ2:WordPressコードでリクエストをフィルタリングする

// functions.php: カスタムWAFフィルター
class WordPress_WAF {

    public function __construct() {
        add_action('init', [$this, 'inspect_request'], 1);
    }

    public function inspect_request(): void {
        if (is_admin() || wp_doing_cron() || wp_doing_ajax()) return;

        $this->block_sql_injection();
        $this->block_xss();
        $this->block_bad_user_agents();
    }

    private function block_sql_injection(): void {
        $patterns = [
            '/(\bUNION\b.*\bSELECT\b|\bSELECT\b.*\bFROM\b)/i',
            '/(\bDROP\b|\bTRUNCATE\b|\bDELETE\b).*\b(TABLE|FROM)\b/i',
            '/\b(OR|AND)\b\s+[\'"0-9]/i',
        ];

        $input = serialize($_GET) . serialize($_POST);

        foreach ($patterns as $pattern) {
            if (preg_match($pattern, $input)) {
                $this->block_request('SQL Injection attempt detected');
            }
        }
    }

    private function block_xss(): void {
        $patterns = [
            '/<script[^>]*>/i',
            '/javascript:/i',
            '/on(load|error|click|mouseover)=/i',
        ];

        $input = serialize($_GET) . serialize($_POST);

        foreach ($patterns as $pattern) {
            if (preg_match($pattern, urldecode($input))) {
                $this->block_request('XSS attempt detected');
            }
        }
    }

    private function block_bad_user_agents(): void {
        $ua = $_SERVER['HTTP_USER_AGENT'] ?? '';

        $bad_agents = ['sqlmap', 'nikto', 'masscan', 'nmap', 'zgrab'];

        foreach ($bad_agents as $agent) {
            if (str_contains(strtolower($ua), $agent)) {
                $this->block_request('Malicious user agent: ' . $agent);
            }
        }
    }

    private function block_request(string $reason): never {
        // ログに記録
        error_log(sprintf(
            '[WAF BLOCK] %s | IP: %s | URL: %s',
            $reason,
            $_SERVER['REMOTE_ADDR'] ?? 'unknown',
            $_SERVER['REQUEST_URI'] ?? 'unknown'
        ));

        // 403を返して終了
        wp_die('Access Denied', 'Forbidden', ['response' => 403]);
    }
}

new WordPress_WAF();

ステップ3:ログイン試行をThrottleする

// ログイン失敗をトランジェントで記録・制限
add_filter('authenticate', function(WP_User|WP_Error|null $user, string $username): mixed {
    if (is_wp_error($user) || !$user) {
        $ip       = sanitize_text_field($_SERVER['REMOTE_ADDR'] ?? '');
        $key      = 'login_attempts_' . md5($ip);
        $attempts = (int) get_transient($key);

        set_transient($key, $attempts + 1, 15 * MINUTE_IN_SECONDS);

        if ($attempts >= 5) {
            // ログに記録
            error_log("[WAF] Login blocked: IP={$ip}, user={$username}");
        }
    }
    return $user;
}, 30, 2);

add_filter('wp_authenticate_user', function(WP_User|WP_Error $user): WP_User|WP_Error {
    $ip      = sanitize_text_field($_SERVER['REMOTE_ADDR'] ?? '');
    $key     = 'login_attempts_' . md5($ip);
    $blocked = 'login_blocked_' . md5($ip);

    if (get_transient($blocked)) {
        return new WP_Error('too_many_attempts', '試行回数が多すぎます。15分後にお試しください。');
    }

    $attempts = (int) get_transient($key);
    if ($attempts >= 5) {
        set_transient($blocked, true, 15 * MINUTE_IN_SECONDS);
        return new WP_Error('too_many_attempts', '試行回数が多すぎます。15分後にお試しください。');
    }

    return $user;
});

ステップ4:ModSecurityを有効化する(Apache)

# ModSecurityインストール
sudo apt install libapache2-mod-security2
sudo a2enmod security2

# OWASP Core Rule Set(CRS)をインストール
sudo apt install modsecurity-crs
sudo cp /etc/modsecurity/modsecurity.conf-recommended /etc/modsecurity/modsecurity.conf

# modsecurity.conf の編集
sudo sed -i 's/SecRuleEngine DetectionOnly/SecRuleEngine On/' /etc/modsecurity/modsecurity.conf

# WordPress用のホワイトリスト(誤検知防止)
# /etc/modsecurity/wordpress-whitelist.conf
# SecRule REQUEST_URI "@beginsWith /wp-admin" "id:1000,phase:1,nolog,allow"

sudo systemctl restart apache2

# ログ確認
tail -f /var/log/apache2/modsec_audit.log

ステップ5:セキュリティヘッダーを追加する

// セキュリティヘッダーを出力
add_action('send_headers', function(): void {
    // XSS攻撃を防ぐ
    header('X-Content-Type-Options: nosniff');
    header('X-XSS-Protection: 1; mode=block');

    // クリックジャッキング防止
    header('X-Frame-Options: SAMEORIGIN');

    // Content Security Policy
    header("Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' https://www.google-analytics.com; style-src 'self' 'unsafe-inline'");

    // HTTPS強制
    header('Strict-Transport-Security: max-age=31536000; includeSubDomains');

    // リファラー制限
    header('Referrer-Policy: strict-origin-when-cross-origin');
});

注意事項

  • 誤検知: WAFフィルターが厳しすぎると正常なリクエストもブロックされます。最初は検出のみで運用し、ログを確認しながら調整してください
  • プラグイン競合: Wordfence・Sucuriなど既存のセキュリティプラグインを使用している場合はカスタムWAFとの重複に注意してください
  • 管理画面: 管理画面でのXSS/SQLパターンチェックは投稿編集などを妨げる場合があります。is_admin()での除外を確実に行ってください

まとめ

WAFの実装は「Nginxのレート制限→WordPressコードによるフィルター→ログイン試行制限→セキュリティヘッダー」の4層で構成します。特にブルートフォース攻撃の防止と不正なリクエストのブロックは早急に対応してください。関連記事:Cloudflare設定ガイド

お気軽にご相談ください

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