Drupal 9: 一般的なエクスプロイトの経路をブロックする方法

この記事は「 Drupal 9: Blocking common exploit paths 」の翻訳です。
Table of content

Drupal サイトを長く運営していると、Drupal とは関係のないいくつかのパスが多くのトラフィックを受けていることに気付くのではないでしょうか。これらのパスはすべて「ページが見つかりません」というエラーにつながるため、サーバーのリソースを消費するだけの影響しかありません。wp-login、xmlrpc.php、phpBB/page_header.php、postnuke/article.php などのパスがよく見られますが、その他にも多数のパスがあります。これらのリクエストは明らかにボットによるもので、どのような種類の CMS が使用されているか、それを悪用できるかどうかをボットが調べるためにサイトを調査した形跡です。

インターネットがこのような状態であることは少々残念ではありますが、これはウェブサイトを管理する際に注意すべきことの一例を示しています。不正なユーザーや、さらにはボットが、あなたのサイトやサーバーを継続的に探り悪用しようとしています。だからこそファイアウォールを設置し、ソフトウェアを最新の状態に保つ必要があるのです。さもなければサイトをクラックしてデータを流出させようとする多くの輩がやってきます。

ボットは脆弱性への攻撃だけでなく、ドキュメントルート 内のファイルも調査します。データベースダンプ、バックアップファイル、テストファイル、一般的なモジュールが使用するディレクトリのなかでアクセス制限がかけられていないものなどに対して定期的にアクセスを試みます。隠蔽によるセキュリティーは、セキュリティー対策ではないという重要な事実を認める必要があります。ユーザーが ドキュメントルート 内のデータベースダンプを見つけられないと考えるのは甘えに他なりません。

あなたの Drupal サイトでどのようなパスに対するリクエストが多いかを調べるには、以下の SQL クエリーを使用します。これは、DBLog モジュールがサイト上で有効になっていることを前提としています。このクエリーを実行すると、あなたのサイトの 404 (page not found) ページのランク付きの一覧が返されます。

SELECT location, count(location) AS location_count
FROM watchdog AS watchdog
WHERE type = 'page not found'
AND message NOT LIKE 'sites/all/%'
AND message NOT LIKE 'sites/default/%'
AND message NOT LIKE '%/styles/%'
GROUP BY location
ORDER BY location_count DESC
LIMIT 50;

先週だけでも、このサイトの wp-login.php へのリクエストを 300 件以上受けましたが、このパスに対応するものが存在しないため、リクエストは 404 応答でした。ただしこの場合でも Drupal のブートストラップが実行されて貴重なサーバーリソースを消費してしまうので、対策を試みることにしました。

この問題を解決するには、いくつかの方法があることがわかりました。

Fast 404

デフォルトでは、Drupal には高速に 404 を返す機能が搭載されており、Drupal を起動せずに軽量のページロードを返すことができます。このモジュールは Drupal 7 から存在しており、Drupal 8 ではコアに移行されました。

settings.php ファイルを見ると、fast_404 のための設定項目がいくつかあります。

  • exclude_paths - これは正規表現で、Fast 404 システムに反応してほしくないものを除外することができます。デフォルトでは、styles と system/files ディレクトリが含まれており、これらのディレクトリーからダイナミックコンテンツを正しく提供することができます。
  • paths - Fast 404 ページをトリガーしたいパスのリストを含む正規表現です。デフォルトはファイル拡張子のリストです。
  • html - 404 エラーが発生した場合に、サイトが提供する実際の HTML です。
Fast 404 機能を有効にするには、以下の行のコメントを解除する必要があります。

$config['system.performance']['fast_404']['exclude_paths'] = '/\/(?:styles)|(?:system\/files)\//';
$config['system.performance']['fast_404']['paths'] = '/\.(?:txt|png|gif|jpe?g|css|js|ico|swf|flv|cgi|bat|pl|dll|exe|asp)$/i';
$config['system.performance']['fast_404']['html'] = '<!DOCTYPE html><html><head><title>404 Not Found</title></head><body><h1>Not Found</h1><p>The requested URL "@path" was not found on this server.</p></body></html>';

現在、404 レスポンスを生成するページにアクセスすると、探していたファイルが存在しないことを伝える、切り取られたページが表示されます。

The 'not found' message returned by fast_404

上のスクリーンショットで分かるように、この機能の出力は完全ではありません。この 404 ページは、サイトにファイルが存在しない場合に発行されるため、一般ユーザーに見つかってしまう可能性があります。サイトのブランドに基づいた 404 ページにするためには、ある程度時間をかける必要があります。また、404 ページは Drupal のブートストラップのかなり初期の段階で作成されるため、Drupal のページで使用するモジュールやブロックのすべてにはアクセスできないことを覚えておいてください。また、Drupal のサービスにもアクセスできないので、データベースにアクセスしてサイト名を参照することも簡単にはできません。Fast 404 は、robots.txt モジュールのようないくつかの他のモジュールと衝突することもあります。robots.txt が動く前にFast 404 機能が 404 レスポンスを発行しないように、robots.txt ファイルに exclude ルールを追加する必要があります。

一般的な 404 パスをこの機能に追加するには、fast_404 設定の path 設定項目に追加する必要があります。

$config['system.performance']['fast_404']['paths'] = '/^(wp-login|wp-admin|xmlrpc|phpbb)|\.(?:php|txt|png|gif|jpe?g|css|js|ico|swf|flv|cgi|bat|pl|dll|exe|asp)$/i';

ユーザーが wp-login などのページにアクセスすると、サイトのリソースを消費しないように、簡略化された 404 ページが表示されます。

結局のところ、Fast 404 システムにはいくつかの制限がありますが、無用なページへのアクセスを防ぐためには必要なソリューションかもしれません。ただしこのシステムは設定に手間がかかり、サイトの他の部分(たとえば robots.txt)と干渉することがあるため、よほど特殊な用途でない限り、使用はお勧めできません。

.htaccess で 404 を返す

もちろん、Drupal を起動する前に 404 レスポンスを発行することもできます。これは、Web サーバーにルールを追加したり、Varnish サーバーに VCL ルールを追加したり、Cloudflare の設定に例外を追加したり、あるいはハードウェアファイアウォールを使ってリクエストをブロックしたりと、さまざまな方法で行うことができます。

これらのパスに対する 404 レスポンスを .htaccess ファイルに追加するには、Drupal コアのデフォルトの .htaccess ファイルの「RewriteBase /」行の直後に次のように記述します。

RewriteRule ^wp-admin/?- [L,R=404].

これを設置すると、ユーザーは Drupal ではなくサーバー自身が発行した 404 ページを受け取ることになります。繰り返しになりますが、Fast 404 モジュールの場合と同様に、そのページのデザインや内容を整えるために手間をかける必要が生じます。ですがこの方法は Drupal による 404 ページの発行を代行するため、最終的には 404 ページ発行のためのリソースの削減につながります。

Perimeter モジュール

さらに次のレベルに進みたい場合は、Perimeter Defence モジュールがお勧めです。このモジュールは非常にシンプルな管理インターフェースを持ち、ユーザーをサイトから完全にブロックするようなパスを追加することができます。

以下は Perimeter モジュールの管理インターフェースです。

Screenshot of the Perimeter module admin UI

ユーザーがたまたまアクセスしてしまったときにブロックしたいパスがあれば、上記のリストを更新します。

モジュールを有効にすると、これらのパスのひとつ、たとえば wp-login にアクセスしたユーザーには、次のようなレスポンスが表示されます。

Message returned by the Perimeter module

Perimeter Defenceモジュールは、Drupalのコアモジュールである Ban モジュールと連携しています。これは、ユーザーがサイトから禁止されると、永久に禁止されることを意味します。このため、このモジュールを使用する前に、データベースレベルのアクセス権があることを確認する必要があります。このモジュールは、あなたがログインしているかどうかは気にせず、あなたの IP アドレスを禁止してしまいます。

もしサイトのユーザーが禁止されてしまった場合でも、シンプルな管理インターフェイスから IP アドレスの管理が可能です。

The list of banned IPs recorded by the Perimeter module

削除したい IP アドレスの横にある ‘delete’ ボタンをクリックすると、その IP のアクセス禁止が解除され、その IP でサイトにアクセスしているユーザーは再びアクセスできるようになります。この方法の利点は、既知のエクスプロイトパスを使ってサイトを訪問しようとするユーザーを自動的に禁止することです。このモジュールはブラックリストを利用したアプローチを取りたい場合に適しており、非常に良いパフォーマンスを発揮します。唯一の欠点は(誤って自分を簡単に禁止してしまうことを除けば)、データベースへのアクセスが使用されているため、ページロードがリソースを消費してしまうことです。

注意点としては、管理画面で誤った正規表現を入力してしまうと、404 ページでエラーが発生してしまうことです。インターフェイスの各行を個別の正規表現として扱えば、問題を発見することができるはずです。

また、CDN や Cloudflare などのプロキシを利用している場合は、ユーザーの IP アドレスが正しく設定されていることを確認してください。これは、禁止対象の悪意あるユーザーエージェントではなく、プロキシのトラフィックを禁止する可能性があるからです。

なお Drupal 7 を使用している場合は、path2ban が同等のモジュールになります。

最終的には、Perimeter Defence モジュールをインストールして様子を見ることにしました。このモジュールがサイトで有効になってからわずか 24 時間後には、30 の IP アドレスをブロックしました。ということで、くれぐれもこのサイトの上記のパスにアクセスしないようお願いします。永久にブロックされてしまいますので。ブロックされた IP アドレスは時々消去して、誤ってアクセスしてしまった人がサイトに戻れるようにします。

 
フッターの採用情報
 

関連コンテンツ