前章までの秘匿化によって、WebサイトからかなりWordPressの存在を匂わす要素がなくなって来たが、完全な秘匿化まではまだまだ足りない部分がある。その一つがプラグインだ。

 基本的にWordPressはブログツールとしてのCMSなので、投稿・コメント管理とユーザ管理ぐらいしかデフォルトで機能を持っていない。なので、Webサイト向けの構築ツールとして利用する場合、お問い合わせ機能や、SEO施策、セキュリティ対策などは自前で準備する必要があるのだ。そして、それらの拡張機能はプラグインとして配布されているものを都度インストールするのが一般的である。もちろん自分でプラグインを開発してそれをインストールして使うことも可能だ。いずれにせよ、それらプラグインによる拡張機能を付与したWordPressサイトでは、使用しているプラグインの存在を隠蔽する必要が出てくる。

 なお、プラグインの隠蔽はサイトの秘匿化だけではなく、サイトのセキュリティ向上にも役立つことを覚えておいてほしい。WordPressのプラグインの種類はまさに千差万別あるが、それは品質においても同様で、セキュリティホールが残ったまま更新されていないものもあれば、脆弱性が発見されてから対応されるまでの期間もまちまちだ。そのため、WordPressサイト内で使用されているプラグインが露見することは、そのプラグインに脆弱性が見つかった場合に、そこからサイト全体がハッキング・クラッキングされたりする可能性がありえるのだ。使用中のプラグインを秘匿化しておくと、それらのセキュリティリスクにもある程度の防波堤を設けておくことができる。

プラグインの選別が重要

 WordPressサイトの秘匿化を目指すうえで、利用するプラグインを厳選することが重要だ。理想的には何もプラグインを導入しなければ、この章の施策は不要になり、高い秘匿化効果を得られることになるのだ。ただ、素のWordPressの機能のみでWebサイトを運営するのは効率的ではない。もし高度な機能を備えたWebサイトを構築する場合などは、プラグインで機能を強化しないと、要件を満たすことはほぼ不可能だろう。

 まぁ、一つの方向性として、機能強化や追加はプラグインではなくテーマ側でやるというアイデアもある。しかし、この方法で構築してしまうと、外見の制御と各種機能の分離がしづらくなり、サイトの保守性が低下する可能性が高く、デザインリニューアルやUI拡張といった柔軟性も失われてしまうのでおすすめしない。そう云う理由もあって、私個人としては高機能なテーマを利用するのは嫌厭している。

 結論として、WordPressではプラグインを使っていくことがセオリーになるのだが、導入するプラグインは必要最小限に抑え、機能要件や性能に応じて厳しく選別する必要がある。それには構築するWebサイトの存在目的を明確化することだ。作るサイトが個人ブログなのか、コーポレートサイトなのか、はてまたECサイトなのか。そして、そのサイトに最低限必要な機能は何なのか。この二つの指針だけでもはっきりさせておくと、プラグインの選別はそう難しいものではないだろう。

 今回のチャレンジでは、最もWordPressっぽいであろうブログサイトを秘匿化するという方針で進めている。ただし、機能的にいくつかは補足しておかないといけない点がある。
 前章でも触れたが、私的にはよっぽどのPVとAUが見込めないサイトでは、コメント機能は不要だと考えている。ほとんど利用されないであろうコメント機能に対して、コメントスパム対策にプラグインを導入するのはパフォーマンス的にもセキュリティ的にも無駄でしかない。いくらデフォルトでバンドルされている「Akismet Anti-Spam」プラグインを有効にするだけだとしてもだ。
 というわけで、今回はAkismetプラグインは使わない。代わりにWordPressのコメント機能を無効化する「Disable Comments」を導入する。このプラグインでWordPress上のすべてのコメント機能を無効化すると、もはや現在利用されているのかも不明なトラックバックやピンバックといったレガシーな機能も停止されるので、秘匿化にとってもメリットが多い。

 しかしながら、「Disable Comments」プラグインは管理画面側や内部処理をフィルタするものなので、(HTMLソース内等の)フロントエンド側に影響を及ぼさない。こういう系統のプラグインは、特に秘匿化の対応をする必要がない。ある意味、とても優秀なプラグインでもあるのだが、これだとプラグイン秘匿化の知見にならないので、実際にフロントエンド側に影響を加えるようなプラグインを例に秘匿化を行なってみよう。

Jetpackプラグインを隠蔽する

 WordPressサイトを構築する際によく使われるプラグインと言えば「Jetpack by WordPress.com」ではないだろうか。Jetpackはサイト分析・統計やCDN、画像最適化、セキュリティ強化、SEO施策等々のWebサイト構築に際して有益な各種プラグインがパッケージングされたWordPress公式のプラグインセットで、サイトの利用シーンに応じて各機能のON/OFFができる。他にプラグインを探さなくても大抵の要件に対応できてしまうので、利用している人も多いと思う。
 私個人的には、導入時にWordPress.comコミュニティの登録アカウントへの連携を要求される仕様と、その巨大過ぎるリソース量によるパフォーマンス懸念などから、いつもは嫌厭しているプラグインだ。
 今回はあえてこのプラグインを導入して秘匿化を行なってみる。早速、管理画面からJetpackを新規インストールして、有効化後、WordPress.comのアカウントに連携してみた(アカウントがない場合は無料なので作成してしまえばよい)。ちなみに、ローカルPC環境やアクセス制限された環境ではアカウント連携ができないので、wp-config.phpdefine( 'JETPACK_DEV_DEBUG', true );の設定を追加してデバッグモードを有効化すると機能制限を受けるが、使えるようになる。
 なお、WordPress.comとのアカウント連携にREST APIを使うので、第2章の秘匿化時に作成したconceal.php内で、USE_REST_API定数を有効化しておかないとJetpackの導入ができなくなるので注意が必要だ。

サイトアクセラレータを有効化していない場合

 さて、Jetpackを有効にする(「パフォーマンス」設定で「サイトアクセラレータを有効化」にチェックが入っていないデフォルト状態)と、フロントエンドのHTMLソースにいくつか影響が出る。特に秘匿化を阻害するのが下記のリソース読み込みだ。
 

<link rel="stylesheet" href="https://{Domain_Name}/addons/jetpack/modules/theme-tools/compat/twentytwenty.css?ver=8.2" media="all">
<link rel="stylesheet" id="jetpack_css-css" href="https://{Domain_Name}/addons/jetpack/css/jetpack.css?ver=8.2" media="all">
<style type="text/css">img#wpstats{display:none}</style>

 リソースパスのURL中やタグのID属性にjetpackの表記があるのがいただけない。また、Twentyシリーズのテーマを使っているとtwenty*.cssが読み込まれてしまう。これらの表記は、そのサイトでWordPressが使われていることを匂わせてしまうので隠蔽したいところだ。なお、インラインスタイルの<style>タグはJetpackのサイト統計情報機能用のビーコン画像を非表示にするためのものだ。今回はこれらすべてを秘匿化する。

 まず、jetpackの表記消しから対策して行こう。プラグイン名をフィルタするにあたっては、任意な別名を付けてしまうとシステマチックな管理が難しくなるので、一律でオリジナルプラグイン名をハッシュ化した文字列を利用することにする。

# 文字列 jetpack のmd5ハッシュ値を求める
$ echo -n 'jetpack' | md5
388f3517eda5bf26174654c89c9d201f
# もしくは
$ php -r "echo md5('jetpack');"
388f3517eda5bf26174654c89c9d201f

# 文字列 jetpack のsha1ハッシュ値を求める
$ echo -n 'jetpack' | openssl sha1
806aec3e1081c90de4ae10abb1f5882480b6774f
# もしくは
$ php -r "echo sha1('jetpack');"
806aec3e1081c90de4ae10abb1f5882480b6774f

# 文字列 jetpack のcrc32ハッシュ値を求める
$ php -r "echo hash('crc32', 'jetpack');"
d955eeb2

 どのハッシュ値を使っても良いのだが、短い方が見やすい&管理しやすいので、今回はプラグイン名の「jetpack」をcrc32アルゴリズムでハッシュ化したd955eeb2を使ってフィルタリングしてみる。

# ---
# 11. 隠蔽するプラグインのルーティング設定
# ---
RewriteRule ^addons/d955eeb2/public-style.css(.*)$ /app/wp-content/plugins/jetpack/modules/theme-tools/compat/twentytwenty.css$1 [L]
RewriteRule ^addons/d955eeb2/main.css(.*)$ /app/wp-content/plugins/jetpack/css/jetpack.css$1 [L]
RewriteRule ^addons/d955eeb2/(.*)$ /addons/jetpack/$1 [L]

 まず、.htaccessにjetpackのディレクトリパスと、隠蔽が必要なファイル名の個別パスをリライトする設定をそれぞれ追加する。注意点としては、個別のファイルパスの設定を行った後、最後にプラグインのディレクトリパスをリライトすることぐらいだ。

 次に、conceal.phpにフィルタリング処理を追加する。まず、after_setup_themeのアクションフック内のバッファリングを拡張する。

add_action( 'after_setup_theme', function() {
    (...中略...)
    ob_start( function( $buffer ) {
        (...中略...)
        // Skip the filtering below on the admin panel of WordPress
        if ( is_admin() ) {
            return $buffer;
        }
        // Remove the `id='jetpack*'` attributes
        $buffer = preg_replace( '/id=(\'|")jetpack.*?(\'|")/', '', $buffer );
        // Remove inline style tag for "#wpstats" in jetpack
        $buffer = preg_replace( '/<style.*?>img#wpstats.*?>/', '', $buffer );
        return $buffer;
    } );
}, PHP_INT_MAX );

 そして、filter_alt_dirs()関数を次のように拡張する。

function filter_alt_dirs( $src ) {
    $src = preg_replace( '@/app/wp-includes/@', '/'. basename( WP_CONTENT_DIR ) .'/'. ALT_WP_INCLUDES_DIR .'/', $src );
    $src = preg_replace( '@/app/wp-admin/@', '/'. basename( WP_CONTENT_DIR ) .'/'. ALT_WP_ADMIN_DIR .'/', $src );
    // For concealing the Jetpack plugin without CDN
    $active_plugins = get_option( 'active_plugins' );
    foreach ( $active_plugins as $plugin_dispatcher_path ) {
        $_strings = explode( '/', trim( $plugin_dispatcher_path, '.php' ) );
        if ( ! empty( $_strings ) ) {
            $src = preg_replace( '@/'. $_strings[0] .'/@', '/'. hash( 'crc32', $_strings[0] ) .'/', $src );
            if ( 'jetpack' === $_strings[0] ) {
                $src = preg_replace( '@/modules/theme-tools/compat/twenty.*?\.css@', '/public-style.css', $src );
                $src = preg_replace( '@/css/jetpack\.css@', '/main.css', $src );
            }
        }
    }
    return $src;
}

 ただし、この方法も万全ではなく、後述のWordPress.com統計情報用の外部スクリプト読み込みを秘匿化するには次項のCDNリソースフィルタリングも必要になってくる。Jetpackは処理が重厚長大すぎて、およそ秘匿化に向かないプラグインであると云える。

サイトアクセラレータを有効化している場合

 Jetpackの機能の一つに、「パフォーマンス」設定の「サイトアクセラレータを有効化する」という項目がある。これを有効化すると、サイト内に読み込まれる画像やJavaScriptやCSSといった静的リソースが、WordPress.comが提供しているCDNから読み込まれるようになる。これによってCDNにキャッシュ化されたリソースが配信されるようになり、Webページ表示時のパフォーマンスを向上させることができる(と、謳われている)。
 この機能を有効化すると、HTMLの<head>内のリソースパスが下記のように変わってしまう。

<link rel="stylesheet" href="https://c0.wp.com/p/jetpack/8.2.1/modules/theme-tools/compat/twentytwenty.css" media="all">
<link rel="stylesheet" href="https://c0.wp.com/p/jetpack/8.2.1/css/jetpack.css" media="all">
<link rel="dns-prefetch" href="//c0.wp.com">
<style type="text/css">img#wpstats{display:none}</style>

 この場合、前項の対応では秘匿化できない。サイト外部のURLから各種リソースを取得することになるので、サイト内部にしか影響を及ぼさないリライト等では対応できないのだ。では、このケースでは秘匿化ができないかというと、そんなことはない。外部リソース取得を仲介するプログラムを間に挟んでやればよい。

$ cd {Document_Root_Path}
$ touch resources.php
$ touch resources.json
$ chmod 606 resources.json

 ドキュメントルートに外部リソース取得用スクリプトresources.phpを作成して、.htaccessにこのファイルへのアクセス制限設定を追加しよう。

# ---
# 11. 外部リソース取得用スクリプト`resources.php`へのルーティング設定と、
#     リソースリスト`resources.json`へのアクセス制限
# ---
RewriteCond %{ENV:is_allow} ^true$ [OR]
RewriteCond %{ENV:is_allow_referer} ^true$
RewriteRule ^assets/resource/(.*)$ /resources.php?aid=$1 [L]
RewriteCond %{ENV:is_allow} !^true$
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule ^resources\.json$ - [R=404,L]

 resources.phpのコードは下記の通りだ。

<?php
defined( 'CDN_RESOURCES_FILE' ) or define( 'CDN_RESOURCES_FILE', $_SERVER['DOCUMENT_ROOT'] . '/resources.json' );

$assets_id = filter_input( INPUT_GET, 'aid' ) ?: null;
if ( ! empty( $assets_id ) && file_exists( CDN_RESOURCES_FILE ) ) {
    $_resources = @file_get_contents( CDN_RESOURCES_FILE );
    $_resources = $_resources ? json_decode( $_resources, true ) : [];
    if ( array_key_exists( $assets_id, $_resources ) ) {
        $_url = $_resources[$assets_id];
        $ch = curl_init();
        curl_setopt( $ch, CURLOPT_URL, $_url );
        curl_setopt( $ch, CURLOPT_HEADER, 1 );
        curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
        $response  = curl_exec( $ch );
        $curl_info = curl_getinfo( $ch );
        curl_close( $ch );
        $mime_type = $curl_info['content_type'];
        $header_size = $curl_info['header_size'];
        $content = substr( $response, $header_size );
        header( "Content-Type: $mime_type" );
        die( $content );
    }
}
header( "HTTP/1.0 404 Not Found" );

 最後に、conceal.phpを拡張する。まず、after_setup_themeのアクションフック内のバッファリングに下記のコードを追加しよう。

add_action( 'after_setup_theme', function() {
    (...中略...)
    ob_start( function( $buffer ) {
        (...中略...)
        // Skip the filtering below on the admin panel of WordPress
        if ( is_admin() ) {
            return $buffer;
        }
        // Remove inline style tag for wpstats in jetpack
        $buffer = preg_replace( '/<style.*?>img#wpstats.*?>/', '', $buffer );
        return $buffer;
        // Remove the `<link rel="dns-prefetch" href="//c0.wp.com">`
        $buffer = preg_replace( '/<link rel=(\'|")dns-prefetch(\'|").*?>/', '', $buffer );
    } );
}, PHP_INT_MAX );

 そして、JetpackのCDNリソースURLをフィルタするためのfilter_jetpack_cdn()関数を準備して、filter_alt_dirs()関数を拡張する。

defined( 'CDN_RESOURCES_FILE' ) or define( 'CDN_RESOURCES_FILE', $_SERVER['DOCUMENT_ROOT'] . '/resources.json' );

function filter_jetpack_cdn( $url ) {
    if ( preg_match( '@^(https?:|)//(.*?)\.wp\.com(.*)$@', $url, $matches ) === 1 && ! empty( $matches ) && file_exists( CDN_RESOURCES_FILE ) ) {
        $_resources = @file_get_contents( CDN_RESOURCES_FILE );
        $_resources = $_resources ? json_decode( $_resources, true ) : [];
        $_hash = hash( 'crc32', $matches[0] );
        if ( ! array_key_exists( $_hash, $_resources ) ) {
            $_resources[$_hash] = $matches[0];
            @file_put_contents( CDN_RESOURCES_FILE, json_encode( $_resources ), LOCK_EX );
        }
        $url = sprintf( '%s/assets/resource/%s', home_url(), $_hash );
    }
    return $url;
}

function filter_alt_dirs( $src ) {
    $src = preg_replace( '@/app/wp-includes/@', '/'. basename( WP_CONTENT_DIR ) .'/'. ALT_WP_INCLUDES_DIR .'/', $src );
    $src = preg_replace( '@/app/wp-admin/@', '/'. basename( WP_CONTENT_DIR ) .'/'. ALT_WP_ADMIN_DIR .'/', $src );
    if ( ! is_admin() ) {
        // For concealing the Jetpack plugin with CDN
        $src = filter_jetpack_cdn( $src );
    }
    return $src;
}

 なお、今回の施策では管理画面側のCDNリソースフィルタリングは除外してある。というのも、Jetpackのサイトアクセラレータ有効時に管理画面でCDN経由で読み込まれるリソース数が尋常ではなく、サイトのパフォーマンスが大きく低下してしまうからだ。この方法だと、CDNのリソースキャッシュを取得する前に、常にresources.phpによるファイルストリーム処理のオーバーヘッドが発生するため、サーバ負荷が大きくなってしまう。CDNリソース数が増えるとサーバ負荷もそれに比例してしまうため、CDNによるキャッシュリソース配信によるネットワーク高速化メリットが相殺、もしくはそれ以上のサーバ負荷によってマイナス効果になる可能性があるのだ。サイトのパフォーマンスを取るか、秘匿化による「セキュアさ」を取るかのトレードオフとなって来るわけだ。そこで、ユーザログイン時や管理画面アクセス時に秘匿化をあきらめることは一つの折衷案となるだろう。

WordPress.com統計情報のビーコン画像を非表示にし、スクリプト読み込みを秘匿化

 前項の対応で、バッファリング拡張で統計情報用のビーコン画像のインラインスタイルを削除してしまうと、WordPressで出力されるページの最下部にビーコン画像のスマイルマークが表示されるようになってしまう。このビーコン画像は、ページが出力された後に非同期で読み込まれるので、WordPress側の出力処理ではフィルタリング出来ない。そこで、HTMLのDOM変更を監視して画像タグが追加されたタイミングでフィルタリング処理を行うJavaScriptを別途用意しておく必要がある。conceal.phpwp_enqueue_scriptsアクションフックを下記のように拡張しよう。

add_action( 'wp_enqueue_scripts', function() {
    if ( ! is_admin() ) {
        // Remove script tags for block library
        wp_dequeue_style( 'wp-block-library' );
        wp_dequeue_style( 'wp-block-library-theme' );
        // Add new inline scripts into append body
        wp_register_script( 'conceal-footer', '', [], '', true );
        wp_enqueue_script( 'conceal-footer' );
        $_add_script = <<<EOS
const conceal = function(){
    let body = document.body,
        callback = function(mutationsList,observer) {
            mutationsList.forEach(function(mutation) {
                let self = mutation.target;

                if ('childList' === mutation.type) {
                    Array.prototype.forEach.call(mutation.addedNodes,function(elm) {
                        if (elm.tagName === 'IMG' && elm.getAttribute('id') === 'wpstats') {
                            elm.style.display = 'none';
                        }
                    });
                };
            });
            observer.disconnect();
        },
        observer = new MutationObserver(callback);

    observer.observe(body,{childList:true,subtree:true});
};
if (document.readyState === 'complete' || (document.readyState !== 'loading' && !document.documentElement.doScroll)) {
    conceal();
} else if (document.addEventListener) {
    document.addEventListener('DOMContentLoaded',conceal,false);
} else {
    window.onload = conceal;
}
EOS;
        wp_add_inline_script( 'conceal-footer', str_replace( [ "  ", "\t", "\r", "\n" ], '', $_add_script ) );
    }
} );

 ビーコンを消すためだけにここまで手間をかけないといけないのが、Jetpackの難点の一つだ。なぜ、非同期読み込みのビーコンタグ自体をスタイル属性(style="display:none;")を付けてレンダリングさせて非表示化しないのだろうか……? いまだに提供側の意図が読めない謎仕様である(苦笑)

 次に、WordPress.com統計情報のクライアント側処理を行うスクリプトの読み込み部分を秘匿化する。このスクリプトはHTMLの</body>の前あたりに、wp_footerアクションフックを使って出力されている下記のタグだ。

<script type="text/javascript" src="https://stats.wp.com/e-202009.js" async="async" defer="defer"></script>

 このタグを秘匿化するためには、前項で紹介したCDNリソースURLのフィルタリングが必要になる。つまりはresources.phpresources.jsonfilter_jetpack_cdn()関数などの一式が必要ということだ。そのうえで、conceal.php内のafter_setup_themeアクションフックのバッファリングに下記のコードを追加しよう。

add_action( 'after_setup_theme', function() {
    (...中略...)
    ob_start( function( $buffer ) {
        (...中略...)
        // Filter the urls for stats and photon
        if ( preg_match( '@(https?://(stats|pixel)\.wp\.com.*?)(\'|")@', $buffer, $matches ) > 0 && ! empty( $matches[1] ) ) {
            $_origin_url = $matches[1];
            $buffer = str_replace( $_origin_url, filter_jetpack_cdn( $_origin_url ), $buffer );
        }
        return $buffer;
    } );
}, PHP_INT_MAX );

 実際、ここまでJetpackの秘匿化対策をやって来て、かなりカオスになっていることも痛感している。今回は初期導入時とCDN周りの機能のみの秘匿化を紹介したが、もし全ての機能を秘匿化しようとすると機能ごとに個別対応が必要になり、途方もない対策が必要になるだろう。ここまでコストをかけて一つのプラグインを秘匿化する必要性があるのかどうか、是非ともJetpackプラグイン導入時にはよくよく検討して欲しい。

HTMLソースの確認

 さて、前項までの数々の施策によって、Jetpackを含む有効化されているすべてのプラグインで、出力HTML中に読み込まれるJavaScriptやCSSを持つものについては、プラグインディレクトリ名部分がcrc32アルゴリズムのハッシュ値に置き換わるようになる。そのため、.htaccess側でプラグイン名のハッシュ値へのリライト設定をしないと、それらJavaScriptやCSSが読み込まれなくなるので注意が必要だ。
 静的リソースの読み込みが発生するプラグインを追加する都度、.htaccessへのリライト設定を追加する必要があるのは面倒だが、プラグインの秘匿化には今のところ避けては通れない対応なのだ。

 では、ここまで対応した後のJetpackが有効化している状態のWordPressサイトのTOPページの<head>タグを覗いてみる。

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>{サイトのタイトル} – {キャッチフレーズ}</title>
  <meta name="robots" content="noindex,nofollow">
  <link rel="stylesheet" href="https://{Domain_Name}/assets/view/style.css?ver=1.1" media="all">
  <style>(...インラインスタイル(省略)...)</style>
  <link rel="stylesheet" href="https://{Domain_Name}/assets/view/print.css?ver=1.1" media="print">
  <link rel="stylesheet" href="https://{Domain_Name}/assets/resource/aad6cf17" media="all" />
  <link rel="stylesheet" href="https://{Domain_Name}/assets/resource/5eb682a6" media="all" />
  <script src="https://{Domain_Name}/assets/view/assets/js/index.js?ver=1.1" async=""></script>
  <script>document.documentElement.className = document.documentElement.className.replace( 'no-js', 'js' );</script>
</head>
(...中略...)
<script type="text/javascript" src="https://{Domain_Name}/assets/resource/1ba7d4a9" async="async" defer="defer"></script>

 このソースコードから、このページがJetpackプラグインが使われているWordPressサイトであると推測するはかなり難しいのではないだろうか。ただ、秘匿化には成功したものの、対策コストがかなりかかったのも事実だ。前項でも述べたが、Jetpackプラグイン一つを秘匿化するのにここまで様々な施策を入れるのは効率的ではない。さらにこの施策によってサイトのパフォーマンスが低下してしまうというデメリットも出てしまうのもいただけない。
 秘匿化チャレンジの知見としては得るものが大きかったが、結論としてJetpackプラグインの秘匿化は全くオススメしないことを云っておく。
 ちなみに、私のこのチャレンジでは、以降Jetpackは使わないという前提で進めて行くことにする。

プラグイン名のハッシュ化ツール

 プラグイン秘匿化において面倒なのが、プラグイン名をハッシュ化して、そのハッシュ値を使った擬似パスで.htaccessにルーティング設定を行う部分だ。秘匿化の必要なプラグインが1つ2つならばそこまで苦にもならないが、これが増えてくると設定を追加するのが非常に煩わしく大変だ。そこで、利用するプラグイン名を任意のアルゴリズムでハッシュ化して、.htaccessへのルーティング設定例を出力してくれるツールを作ってみた。

Plugin Hasher
https://ka2.org/wp-hasher.php

 このツールは、実際のプラグインディレクトリのパス(ドキュメントルートからのパス)と、隠蔽用に定義した代替プラグインディレクトリのパス(定数WP_PLUGIN_DIRに宣言したパス)を入力して、生成するハッシュのアルゴリズムを選択、秘匿化が必要なプラグインの名前(プラグインディレクトリ内の各プラグインのディレクトリ名=slug)を指定すれば、それぞれのプラグイン名のハッシュ値と、.htaccessに設定するべきの代替ルーティングの設定例を出力してくれるものだ。

プラグイン秘匿化の手順・まとめ

 結論として、プラグインの秘匿化については各プラグインごとの性質によって個別対応が必要になって来る。基本的には、プラグイン用に読み込まれる画像、JavaScript、CSS等の静的リソースのリソースパスを隠蔽する対策が必要だ。
 本項では、まとめとしてプラグイン秘匿化の汎用例的な手順をおさらいしておく。

1. WordPressで出力されたページの静的リソースの読み込みを確認する

 まず、WordPressのページや管理画面をブラウザで開いて、その際に読み込まれている画像やJavaScript、CSSといった静的リソースの読み込みURLにWordPressを使用していることを匂わすものがないかを確認する必要がある。
 具体的には、URL中にプラグイン名や/wp-includes//wp-admin//wp-content/plugins/等のパスが入っているかどうかだ。一応、前項までの施策を行っていれば、wp-includes等のWordPressコアディレクトリは秘匿化済みなので、リソースパス中にそれらのディレクトリ名が出力されることは少ないと思う。また、プラグイン名については、利用しているプラグインが(Jetpackのような)メジャーなものだったり、WordPressを匂わすwp-接頭辞を持っている場合以外であれば、特に手間をかけて秘匿化しなくても構わない。

2. プラグイン名の代替名を決める

 リソースの読み込みURL中に隠蔽が必要なプラグイン名がある場合、そのプラグイン名の代わりとなる代替名を決める必要がある。任意のダミー名を付けても良いが、システム的に管理がしやすく、暗号強度も高いハッシュ値やチェックサム値などを利用するのがベストだろう。
 プラグイン名をハッシュ化するのであれば、下記のツールを使うのが手っ取り早い。

Plugin Hasher
https://ka2.org/wp-hasher.php

3. リソース読み込みURLを代替パスにリライトする

 そして、プラグイン名を代替名に変更した代替パスを有効化する。秘匿化が必要なプラグインごとに.htaccessにリライトルールを追加していくことになる。前述のツールを使えば、ハッシュ化と同時にリライト設定も出力してくれるので、その設定を.htaccessにコピーするだけでOKだ。

4. 再度WordPressページを開いてリソース読み込みURLを確認する

 最後に、ブラウザからWordPressページを再度開いて、リソース読み込みURLがリライトされた代替パスに変更されているか、そのパス経由でのリソース読み込みが成功しているかを確認する。もし問題があるようならば、個別に対応していく必要がある。