baserCMS WafExclusion プラグインを作ってみました。
先だって「4系少しでもご安全に!プロジェクト第 1 弾」として公開した「WafController」プラグイン。
そもそもの制作の動機は、baserCMSやCakePHPがメンテナンスサポートを終了したことで、フレームワークの脆弱性(既知あるいは新たなバグやセキュリティホール)を突いたサイバー攻撃の盾に少しでもなってもらうため。
SiteGuard Lite は、Webサーバーのプラグインモジュールとして直接インストールして動作するホスト型WAF(Webアプリケーションファイアウォール)で、専用のハードウェアは不要なこと、対応するOSとWebサーバーが稼働しているVPS、クラウドなどの各種サーバーに導入できることから、ロリポップ!のような比較的利用料の安価を売りにしている主要な国内レンタルサーバーでは、標準的に利用されています。
WAFは「アプリケーション層」を監視して、SQLインジェクションやクロスサイトスクリプティング(XSS)といったWebアプリケーションの脆弱性を突いたサイバー攻撃や悪意あるプログラム入力を検知・遮断できるすぐれものですが、時にbaserCMSの管理画面上で行う様々な編集、設定作業をそれと誤認して「403誤検知エラー」を出してしまうことがあります。
ならば、(正しい権限の管理者が利用する前提で)管理画面では、WAFを無効化できればいいのでは?と思うのですが、ロリポップ!の場合は、ドメイン単位でのみ有効・無効の切り替えが可能であり、管理画面やユーザー画面など特定のエリア(URL階層)だけで部分的に適用・除外することはできません。
そこで、WAF(SiteGuard Lite)による「管理画面での403誤検知エラー」を解決するため、サーバーインフラへの負荷をかけずに.htaccess を動的に書き換えて、一時的に管理者の接続元IPアドレスのみをWAFのシグネチャ検査から除外(バイパス)させるというコンセプトのbaserCMS4系プラグインを考えた訳です。
旧仕様「WafController」の課題と反省点
旧プラグインの「WafController」では、管理者が画面を完全に閉じたり、規定時間(標準で30分間)無操作の状態が続いた時点で、ブラウザ側の死活監視機能が連動して、サーバー上の .htaccess から自分のIP除外設定だけをピンポイントで自動消去(クレンジング)する、という仕様の設計を試みました。
しかし、現実にはユーザーがブラウザを閉じた際、非同期通信では処理がサーバーへ届く前にプロセスが終了してしまいます。これを防ぐために同期通信(async: false)を利用すると、モダンブラウザのメインスレッドでの同期XMLHttpRequest非推奨規制に抵触し、動作が不安定になります。また、PCのスリープやブラウザクラッシュ、スマホのタスクキル時にはイベント自体が発火しないため、古いIP設定がファイル内に残存し続けるという問題に悩まされ続けました。
また、各管理者のIPアドレスを識別するために md5($ip) のハッシュ値をマーカーに埋め込み、IP個別の除外コードの書き換えや削除を行おうとしましたが、これはPHP側で不要な計算負荷が発生するだけでなく、複数の管理者が同時にアクセスした際の正規表現による置換処理が複雑になり、コードのメンテナンス性を低下させ、度重なるでグレードに悩まされる要因になっていました。
新仕様「WafExclusion」は、自律型ライフサイクル
そこで新仕様への見直しにあたり、「ブラウザが閉じるタイミングで消去する」という不安定な設計を廃止し、「次にアクティブなユーザーがアクセスした瞬間の、軽量な状態チェックと自律リカバリー」へと仕様を180度転換しました。
それによってJavaScriptによるカウントダウンタイマーや終了イベントの監視はすべて全廃し、ページ遷移時やブラウザのタブ復帰時に、現在の接続元IPアドレスが .htaccess 内に存在するかを軽量なダミー要求で「1回だけ」チェックする仕様に変更しています。
ログイン中のユーザーが操作したタイミングで、そのユーザー自身の古いIPブロックをガベレージコレクションメソッドでクリアし、もし自分のIPがファイル内に存在しない場合は、バックグラウンドの通信によって即座に自動再登録(リカバリー)を行います。これにより、不安定なブラウザ切断時の通信に頼ることなく、常にアクティブなユーザーのIPのみが安全に配置されるサイクルを整えました。
どうしても編集作業終了時にIP除外ブロックをクリアしたいという場合は、管理画面から「ログアウト」することで .htaccess 内のすべての登録IPを確実に一括パージ(消去)して初期状態に戻せるようにしました。
さて、あらためて以下が新仕様の「WafExclusion」プラグインです。
ファイル構成が旧プラグインとは全く違うためプラグイン名も変更しています。旧プラグイン「WafController」をもし利用されている場合は、アンインストールして新たに「WafExclusion」をインストールして使ってみてください。
では、いつものようにreadme.mdの写しです。
WafExclusion for baserCMS 4
ロリポップ!レンタルサーバーをはじめとする国内主要サーバーのWAF(SiteGuard Lite)による「管理画面での403誤検知エラー」を、サーバーインフラへの負荷をかけずに解決するbaserCMS 4系専用の自動WAFバイパスプラグインです。
概要
ロリポップ!などのレンタルサーバーのWAFはドメイン単位でしかオン/オフが設定できず、特定の管理画面URLだけを除外することができません。そのため、利便性のためにWAFをオフの状態でサイト運用されるケースが見受けられます。
参考: baserCMS公式サイト - レンタルサーバー毎の設定
本プラグインは、「常時WAFオン」の安全な状態を維持したまま、「管理画面にログインして操作している管理者の接続元IPアドレスのみ」を安全に検知し、.htaccess を動的に書き換えてWAFのシグネチャ検査を一時的に除外(バイパス)します。
通常の公開側アクセス(フロント画面)の裏ではプログラムは起動しないため、サーバーへのディスクI/O負荷、表示速度低下の心配がない軽量・安全設計です。また、管理画面のURLルーティング(カスタムプレフィックス)が変更されている環境であっても、自動的に追従して正常に動作します。
対象サーバー環境
- Apacheモジュール版
SiteGuard Liteが導入されているWebサーバー環境 - ロリポップ!レンタルサーバー(ライト・スタンダードプラン等)
- その他、ヘテムル(heteml)/ さくらのレンタルサーバ / CPIサーバー など
セキュリティ設計と機能
- ピンポイント除外: WAFが解除されるのは、ログイン中の管理者の接続元IPアドレスのみです。
- 多重書き込みの排除: 既に
.htaccess内に自分のIP設定が記述されている場合は、無駄なファイルの再書き込み(上書き保存)を自動でスルーし、サーバーへの負荷を防止します。 - 最軽量の動的自己リカバリー: 複雑なサーバーサイドでの有効期限計算やカウントダウンタイマーを廃止(旧プラグイン)し、ページ遷移やタブ復帰の瞬間にフロントJavaScriptが自律的にIPアドレスの存在を確認し、必要に応じて高速に自動再登録(リカバリー)を実行します。
- 確実なログアウト自動パージ: ログアウトボタンを押したその瞬間に、ブラウザの画面遷移を同期ロックし、.htaccess内のIP除外コードのブロックを全消去(パージ)します。
- カスタムプレフィックスに完全対応: JavaScriptが現在開いているURLパスからプレフィックスを動的に直接抽出してURLを組み立てるため、独自URL(/admin以外)でも正常に稼働します。
- IPv4 / IPv6 完全対応: 接続環境を問わず、正常に動作します。
ファイル構成
app/Plugin/WafExclusion/
├── Config/
│ └── setting.php
├── Controller/
│ ├── WafExclusionApiController.php
│ └── WafExclusionController.php
├── Event/
│ └── WafExclusionControllerEventListener.php
├── Model/
│ └── WafExclusion.php
├── View/
│ └── WafExclusion/admin/
│ └── index.php
└── webroot/
└── js/
└── waf_front_manager.js
動作要件・導入前の準備
本プラグインは、サーバー全体の安全性を維持しながら管理者のアクセスを最適化するため、.htaccess ファイルおよびサーバー環境に対して以下の要件と準備が必要です。
1. サーバー環境の要件
- リバースプロキシ環境(Cloudflare等)でのご利用について:
リバースプロキシやCDNを経由している環境で本プラグインを正常に動作させるには、Webサーバー(Apache)側で
mod_remoteipなどのIP復元設定が正しく行われ、PHPの$_SERVER['REMOTE_ADDR']に「真の接続元クライアントIP」が格納されている必要があります。
2. .htaccess への専用マーカーの配置(推奨)
記述順序の矛盾を防ぐため、.htaccess 内の安全な位置に本プラグイン専用の「挿入マーカー」を記述することを推奨します。
設定手順
サイトのルート直下にある .htaccess ファイルをテキストエディタで開き、以下のコメント行(マーカー)を 1行だけ 手動で書き加えて保存してください。
# [WAF_EXCLUSION_MARKER]
推奨される配置位置
Apacheの書き換えエンジンが有効化される記述の直後に配置するのが安全です。RewriteEngine On のすぐ下、または既存の書き換えルールの直前に配置してください。
記述例:
<IfModule mod_rewrite.c>
RewriteEngine On
# 本プラグイン用のマーカーをここに配置
# [WAF_EXCLUSION_MARKER]
RewriteBase /
RewriteRule ^index\.php\$ - [L]
...
</IfModule>
※ マーカーを配置しない場合は、自動的に .htaccess の最先頭へ除外設定が追記(フォールバック)されます。
3. .htaccess ファイルへの書き込み権限の確認
プラグインが動的に設定の追加・削除を行うため、.htaccess ファイルにPHPからの書き込み権限(サーバー環境に応じた 604 や 644 などの適切なパーミッション)が与えられているか確認してください。
設定ファイルの運用・カスタマイズ
環境、運用に合わせた動作変更を行えるように専用の設定ファイルを設置しています。
- 設定ファイルのパス:
app/Plugin/WafExclusion/Config/setting.php
カスタマイズ可能な項目
WAFバイパス対象グループの追加(target_group_ids)
管理画面の設定画面(WAF除外コントローラ設定)より、対象とするユーザーグループをチェックボックスで選択して保存できます。 手動でファイルを調整する場合は、配列内にユーザーグループのIDを追記してください。最高管理者(ID: 1)は常に強制追加されます。
'target_group_ids' => array(
1, // システム管理者
2 // 例:ユーザーグループID
),
プラグインの仕様と動作ルール
本プラグインは、サーバー全体の安全性を維持しながら管理者のアクセスを最適化するため、以下の仕様と動作ルールに基づいて設計されています。
1. サーバー側WAF設定について
ロリポップ!等のコントロールパネル側でWAFが「有効」になっていることを前提に、.htaccess 側で特定IPの検査をスキップさせる仕組みです。プラグイン有効化後は、サーバー側のWAF設定を「有効(オン)」のまま運用してください。
2. .htaccess の書き込み権限(パーミッション)
公開領域トップにある .htaccess に対し、PHPからの書き込み権限が必要です。通常は標準のパーミッション(604 や 644)のままで動作しますが、万が一除外設定が追記されない場合は、パーミッション設定を確認してください。
3. 端末のIPアドレスが変動した場合の挙動
モバイル回線(4G/5G)や一部のプロバイダ環境では、接続元のグローバルIPアドレスが短時間で変動することがあります。操作中にIPアドレスが変わると、直後の保存処理などで一時的に403エラー(WAF誤検知)が発生する場合があります。
本プラグインのフロントJavaScriptが新しいIPアドレスを自律検知して .htaccess を即座に更新するため、ブラウザの画面を一度リロード(再読み込み)するか、もう一度保存ボタンを押し直すことで、そのまま作業を続行できます。
4. 自律型ライフサイクルとパージ仕様
- 画面遷移時の自律登録: 管理画面内の移動、リロード、またはタブの復帰(隠れていたタブを再度開く)が発生したタイミングでチェックし、WAF除外IPの不足を検知した場合のみ超高速で自動リカバリー(再登録)を実行します。
- ログアウト時の即座消去: 「ログアウト」ボタンを押したその瞬間に、ブラウザの画面遷移を同期ロックし、.htaccess内の登録IPを安全に一括全消去(パージ)します。
5. プラグイン無効化(アンインストール)時の動作
baserCMSの管理画面から本プラグインを「無効化」する際、コアシステムのアラートよりも先に、本プラグイン独自のメッセージが起動します。
- 表示メッセージ:
WafExclusion プラグインを無効化(削除)する準備を行います。よろしいですか?
これにより、アンインストール後に .htaccess 内に不要な記述が残るリスクを100%防ぎ、サーバー環境を確実に安全な初期状態へと復元させます。
導入手順
インストール要件
- CMS本体: baserCMS 4.x
- 動作環境: PHP 7.x 以上(PHP 8.x 対応)
インストール方法
- 本リポジトリをダウンロードし、フォルダ名を
WafExclusionに変更します。 - baserCMS の
app/Plugin/ディレクトリ配下に配置します。 - baserCMS 管理画面の「設定」>「プラグイン管理」より、「WafExclusion」 のインストールボタンをクリックして有効化します。
ライセンス
- MIT License
- Copyright (c) 2021 - 2026 HATTA
HATTANTOCO