wpX SpeedのWAFの6項目|記事の本文に引っかかる文字列を数える

wpX Speedには、WordPressごとにON・OFFできるWAF(Webアプリケーションファイアウォール)があります。攻撃に使われる文字列を含むアクセスを検知して止める仕組みで、検知されるとアクセスが拒否され、エラーページが表示されます。ここで気をつけたいのは、記事の本文そのものに、検知されうる文字列が入っていることがある点です。プログラムの解説記事や、REST APIで記事を送る自動投稿では、保存や投稿が止まる原因になりえます。この記事では、マニュアルに書かれた6つの項目を整理し、当サイトの全記事の本文に、どの種類の文字列がどれだけ含まれるかを数えます。

(確認:wpX Speed マニュアル「WAF設定」。確認日:2026年10月30日。数えたのは当サイト realine.net の公開済みの記事。当サイトはwpX Speedではないため、実際に検知されるかは確かめていません)

マニュアルに書かれている6つの項目

項目 検知するもの(マニュアルの説明)
XSS javascriptなどのスクリプトタグが埋め込まれたアクセス
SQL SQL構文に当たる文字列が挿入されたアクセス
ファイル .htpasswd・.htaccess・httpd.conf など、サーバーの設定ファイルが含まれたアクセス
メール to・cc・bcc などのメールヘッダーに関係する文字列を含んだアクセス
コマンド kill・ftp・mail・ping・ls などのコマンドに関連する文字列が含まれたアクセス
PHP session・ファイル操作に関連する関数など、脆弱性の元になりやすい関数が含まれたアクセス
  • 設定は、管理パネルの「インストール済みWordPress一覧」→ 対象の「設定」→「セキュリティ設定」の「WAF設定」で、項目ごとにONにする
  • 追加・変更は、反映まで最大1時間程度かかる場合がある
  • WAFは最低限の予防策で、不正アクセスをすべて防ぐことを保証するものではない。最新の版の利用などの対応も要る、と書かれている

コード:記事の本文に含まれる文字列を数える

REST APIで取得した記事の本文(編集用のHTML)に、各項目の説明と例に出てくる文字列が含まれるかを数えます。WAFの実際の検知のルールは公開されていないため、ここで使う文字列は、マニュアルの説明から筆者が選んだ目安です。

# wpX Speed の WAF 設定(公式マニュアル)が「検知する」と書いている種類の文字列が、記事の本文にどれだけ含まれるかを数える(読み取りのみ)
# 本文は、REST API の context=edit で取得して保存した JSON(記事ごとの raw)を読む
# 注意:WAF の実際の検知ルールは公開されていないため、ここでの文字列は、マニュアルの説明と例から筆者が選んだもの
# 英単語の前後は「英数字でない」ことで区切る(日本語に続く ping なども数える)
import json
import re
import sys
from collections import Counter

RULES = {
    'XSS(スクリプトタグ)':           r'<script\b',
    'SQL(SQL構文の例)':              r'(?<![A-Za-z0-9_])(select|union|insert\s+into|drop\s+table)(?![A-Za-z0-9_])',
    'ファイル(設定ファイル名)':       r'\.htaccess|\.htpasswd|httpd\.conf',
    'メール(メールヘッダーの例)':     r'(?<![A-Za-z0-9_])(to|cc|bcc)\s*:',
    'コマンド(kill・ftp・mail・ping・ls)': r'(?<![A-Za-z0-9_])(kill|ftp|mail|ping|ls)(?![A-Za-z0-9_])',
    'PHP(session・ファイル操作の例)':  r'(?<![A-Za-z0-9_])(session_[A-Za-z0-9_]+|fopen|file_get_contents|file_put_contents|eval)\s*\(',
}

path = sys.argv[1] if len(sys.argv) > 1 else 'posts.json'
posts = [p for p in json.load(open(path, encoding='utf-8')) if p['kind'] == 'posts' and p['status'] == 'publish']
hits = Counter()
any_hit = 0
examples = {}
for p in posts:
    found = False
    for name, rx in RULES.items():
        m = re.search(rx, p['raw'], re.I)
        if m:
            hits[name] += 1; found = True
            examples.setdefault(name, m.group(0))
    any_hit += found
print(f'公開済みの記事: {len(posts)}本')
for name in RULES:
    print(f'  {name:<34}{hits[name]:>4}本({hits[name] / len(posts):>4.0%})  例: {examples.get(name, "-")}')
print(f'どれか1つでも含む記事: {any_hit}本({any_hit / len(posts):.0%})')

実行の結果(当サイトの公開済みの記事)

公開済みの記事: 401本
  XSS(スクリプトタグ)                         2本(  0%)  例: <script
  SQL(SQL構文の例)                        19本(  5%)  例: Select
  ファイル(設定ファイル名)                        6本(  1%)  例: .htaccess
  メール(メールヘッダーの例)                       0本(  0%)  例: -
  コマンド(kill・ftp・mail・ping・ls)         24本(  6%)  例: ping
  PHP(session・ファイル操作の例)                0本(  0%)  例: -
どれか1つでも含む記事: 45本(11%)
  • 当サイトの記事の約1割(45本)に、どれかの種類の文字列が含まれていた。MT5・Excel・WordPressの解説で、コマンド名(ping など)や英語の「Select」などの語が出てくるため
  • スクリプトタグは2本で、どちらも記事の中にJavaScriptを埋め込んでいる記事だった
  • メールヘッダーの例とPHPの関数の例は、0本だった

文字列を含むからといって、止められるとは限りません。ただ、特定の記事だけ保存や投稿ができない時は、その記事の本文に、どの種類の文字列があるかを見ると、原因を絞りやすくなります(筆者の考え)。

止められた時の切り分け(筆者の考え)

  1. どの記事で起きるかを確かめる:全部の記事で止まるならWAF以外(アクセス制限など)、特定の記事だけならWAFを疑う。REST APIの403の見分け方はエックスサーバーでREST APIが403になる時の確認とREST APIのエラーコードにあります
  2. その記事の本文の文字列を、上のコードで調べる:当てはまる項目が分かれば、その項目だけを一時的にOFFにして試す
  3. 作業が終わったら元に戻す:反映に最大1時間かかる場合がある点に注意する

自動投稿で記事を送る前の確認は自動投稿の前の環境チェックにまとめています。

この記事で確かめていないこと

  • wpX SpeedのWAFが、管理画面での保存やREST APIでの投稿に対して、実際にどの文字列で検知するか
  • 各項目の初期状態(ON・OFF)
  • 検知された時に表示されるエラーページの内容と、ステータスコード

まとめ

  • wpX SpeedのWAFは、XSS・SQL・ファイル・メール・コマンド・PHPの6項目を、WordPressごとにONにできる。反映まで最大1時間程度
  • 当サイトの記事の約1割に、どれかの項目に当たりそうな文字列が含まれていた。解説記事では起こりうる
  • 特定の記事だけ保存・投稿できない時は、本文の文字列を調べ、該当する項目だけを一時的にOFFにして試し、終わったら戻す

本記事はwpX Speedの公式マニュアルの記載と、当サイトの記事の集計にもとづく整理です。料金とプランはwpX Speedの料金とプランにまとめています。