KUSANAGIでWAF(NAXSI)を使う

KUSANAGIのバージョン8.4.0から、WAF(Web Application Firewall、ウェブアプリケーションファイアーウォール)が使用出来るようになりました。アップデートされた状態で、新規プロビジョニングしたWordPressが対象です。

WAFはウェブサイトの改ざんなどを目的とした悪意ある通信を検知して遮断します。システムに脆弱性があった場合も、WAFで防げる場合があります。(100%安全が保障されるものではありません)

セキュリティを高める為にはWAFを有効にした方が良いのですが、通常の操作が誤検知によって遮断されることがあり、その場合は除外ルール(ホワイトリスト)を作成して追加をするといった作業が必要になります。

この記事では、KUSANAGIでWAFを使用する際に必要と思われる情報について、公式ドキュメントを若干補足する形で記述しています。なお、サーバーとしてNginxを選択しておりますので、WAFの除外ルール追加作業について、Apacheを選択した場合は異なりますのでご注意下さい。また、基本的にroot権限が必要になりますので、rootに切り替えておくか、都度sudoでコマンドを実行して下さい。

また、KUSANAGIのバージョンアップにより、仕様が変更になる可能性があります。(執筆時点のバージョンは8.4.0-3です)

KUSANAGIのWAFを有効にする

$ kusanagi waf on

KUSANAGIのWAFを無効にする

$ kusanagi waf off

KUSANAGIのWAFの状態を確認する

$ kusanagi status

※”*** WAF ***”の欄で確認します。

KUSANAGIのWAF関連設定ファイル

KUSANAGIのNAXSI設定ファイル

/etc/nginx/conf.d/kusanagi_naxsi_core.conf

ただし、これは下記のNAXSIが攻撃を検知する為のルールを記述した設定ファイルを読み込むだけのファイルです。

/etc/nginx/naxsi.d/naxsi_core.rules.conf

NAXSI共通設定ファイル

  • /etc/nginx/naxsi.d/common/default.conf
  • /etc/nginx/naxsi.d/common/user.conf ※ユーザー編集用

これらのファイルは、NAXSIのWordPress向け除外ルール設定ファイル(”/etc/nginx/naxsi.d/wordpress/default.conf”)で読み込まれます。よって、NAXSI公式ドキュメントにならって、このファイル内の記述をNginxの設定ファイルに記述するといった作業は、必要ありません。

NAXSI一般設定ファイル

  • /etc/nginx/naxsi.d/general/default.conf
  • /etc/nginx/naxsi.d/general/user.conf ※ユーザー編集用

NginxのHTTPおよびSSLの設定ファイル

  • /etc/nginx/conf.d/【プロビジョニング名】_http.conf
  • /etc/nginx/conf.d/【プロビジョニング名】_ssl.conf

これらのファイルで、NAXSIのWordPress向け除外ルールを記述したファイルを読み込んでいます。

NAXSIのWordPress向け除外ルール設定ファイル

  • /etc/nginx/naxsi.d/wordpress/default.conf;
  • /etc/nginx/naxsi.d/wordpress/user.conf; ※ユーザー編集用

default.confでNAXSI共通設定ファイル(”/etc/nginx/naxsi.d/common/*.conf”)を読み込んでいます。

除外ルールを作成する

WAFによって通信が遮断されるとHTTPステータスコードとして503が返されます。(KUSANAGIデフォルト設定の場合)私がWAFを有効にした際、テーマ追加画面へのアクセス時に、503のエラー画面にはなりませんでしたが、テーマ一覧が表示されない不具合が起きました。

通常の操作が遮断されてしまう場合は、除外ルールを追加する必要があります。なお、KUSANAGIには予め除外ルールが設定されていますが、個々で対応しなくてはならない機会は十分に起こりうるでしょう。具体的な方法としては大きく分けて下記2つあります。

  • nxapi/nxtoolを使用
  • 自分でエラーログを読んで解析

前者は追加で色々インストールしたり、KUSANAGIの環境に合わせて公式ドキュメントとは違うやり方で作業する必要があります。

なお、KUSANAGI(WordPress)へのアクセスは全てHTTPSになる環境を前提としています。推奨されませんが、HTTPでのアクセスも許可している場合、以降の作業フロー内において、Nginxのhttp用conf及びエラーログファイルも作業の対象になると、補完してお読み下さい。

nxapi/nxtoolを使用して除外ルールを作成

nxapi/nxtoolの詳細については、GitHubのWiki(英語)を参照して下さい。

naxsi/nxapi at master · nbs-system/naxsi · GitHub

1.NAXSIを学習モードにする

記述内容(追記)

LearningMode;

対象ファイル

/etc/nginx/naxsi.d/common/user.conf

2.ログの出力レベルを変更

記述内容(コメントアウトと追記)

#error_log /home/kusanagi/【プロビジョニング名】/log/nginx/ssl_error.log warn;
error_log /home/kusanagi/【プロビジョニング名】/log/nginx/ssl_error.log debug;

対象ファイル

/etc/nginx/conf.d/【プロビジョニング名】_ssl.conf

3.再起動

$ nginx -s reload

4.nxapiをインストール

KUSANAGIのnaxsiに同梱されていないので、自分でnaxsiをダウンロードして入手します。

$ wget https://github.com/nbs-system/naxsi/archive/master.zip

解凍し、nxapiディレクトリを下記場所にコピーして作業を進めます。

/etc/nginx/naxsi.d/

5.nxapiの設定ファイル書き換え

記述内容(書き換え)

“rules_path” : “/etc/nginx/naxsi.d/naxsi_core.rules.conf”,

対象ファイル

nxapi.json

6.Javaをインストール

$ yum -y install java-1.8.0-openjdk java-1.8.0-openjdk-deve
path確認
$ dirname $(readlink $(readlink $(which java)))

7.Javaの環境変数の設定

確認したpathにおいて、/jre/bin の手前の部分を記述します。
参考:https://qiita.com/Esfahan/items/60cf425514c66553bd42

記述内容(書き換え)

export JAVA_HOME=【確認したpathの/jre/bin の手前の部分】

対象ファイル

/etc/profile

8.ElasticSearchをインストール

2018年10月某日の時点で、nxapiは最新のElasticSearchに正式対応出来ていませんでした。nxapiのファイルを書き換えて対応出来るようですが、過去のバージョンを探して利用する方が良いかもしれません。pyファイルのソースコードを読むと、Ver 1,2,5に対応しているらしい事が分かりました。

Ver 5.xの最終と思われるもの

https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-5.6.12.tar.gz

ダウンロードして適当な場所へ解凍します。

9.ElasticSearch用のユーザーを用意

ElasticSearchはrootで起動出来ないので、別のユーザーで実行するか専用のユーザーを作っておきます。

10.ElasticSearch起動

解凍したElasticSearchのbinディレクトリに移動しておきます。

$ ./elasticsearch

暫く起動ログが表示されて止まるのを待ちます。

11.ElasticSearch起動確認とnxapiインデックス作成

$ curl -XGET http://localhost:9200/

jsonが返されることを確認したら下記を実行します。

$ curl -XPUT ‘http://localhost:9200/nxapi/’

12.ElasticSearchのPythonライブラリをインストール

$ pip install elasticsearch

13.エラーログを読み込む

nxapiディレクトリへ移動しておき、エラーログを読み込みます。

$ ./nxtool.py -c nxapi.json –files=/home/kusanagi/【プロビジョニング名】/log/nginx/ssl_error.log

下記コマンドで読み込んだデータを確認します。

$ curl -H ‘Content-Type:application/json’ “http://localhost:9200/nxapi/events/_search?pretty” -d ‘{}’

14.ElasticSearchにおけるFielddataを有効にする

ElasticSearchのバージョン5以上からFielddataがデフォルトで無効になったようです。これを手動で有効にしないと、次項の除外ルール生成コマンドが通りません。具体的には下記のようなエラーが発生します。

elasticsearch.exceptions.RequestError: RequestError(400, u’search_phase_execution_exception’, u’Fielddata is disabled on text fields by default. Set fielddata=true on [id] in order to load fielddata in memory by uninverting the inverted index. Note that this can however use significant memory. Alternatively use a keyword field instead.’)

この問題を解決するには下記コマンドを実行します。

$ curl -H “content-type: application/json” -X PUT \
> -d ‘{ “properties”: { “id”: { “type”: “text”, “fielddata”: true } } }’ \
> “http://localhost:9200/nxapi/_mapping/id?update_all_types”

参考:https://www.smwenku.com/a/5b864e152b71775d1cd4dd24

15.除外ルールの生成

$ ./nxtool.py -c nxapi.json -s 【対象ドメイン】 -f –filter ‘uri /’ –slack

uriフィルターで、エラーが発生したページを指定して生成出来ます。例えば、”/wp-admin/admin-ajax.php”等です。

コマンド実行後に表示される、”BasicRule”で始まる行をコピーして、除外ルール設定ファイルに追記します。

追記したら、学習モードを無効化し、エラー出力レベルも元に戻し、Nginxを再起動しておきましょう。

自分でエラーログを読んで解析

ここでは、テーマ追加画面で不明なエラーが発生した事象を例として説明します。

1.ログの確認

ログファイル

/home/kusanagi/【プロビジョニング名】/log/nginx/ssl_error.log

エラーログ
[error] 1911#0: *1 NAXSI_FMT: ip=IPアドレス&server=ドメイン&uri=/wp-admin/admin-ajax.php&learning=0&vers=0.56&total_processed=4&total_blocked=1&blo ck=1&cscore0=$XSS&score0=8&zone0=BODY|NAME&id0=1310&var_name0=request%5Bper_page%5D&zone1=BODY|NAME&id1=1311&var_name1=request%5Bper_page%5D, client: IPアドレス, server: ドメイン, request: “POST /wp-admin/admin-ajax.php HTTP/2.0”, host: “ドメイン”, referrer: “https://ドメイン/wp-admin/theme-install.php?browse=featured”

見やすいように整形してみます。

[error] 1911#0: *1 NAXSI_FMT: ip=IPアドレス
&server=ドメイン
&uri=/wp-admin/admin-ajax.php
&learning=0
&vers=0.56
&total_processed=4
&total_blocked=1
&blo ck=1
&cscore0=$XSS
&score0=8

&zone0=BODY|NAME
&id0=1310
&var_name0=request%5Bper_page%5D

&zone1=BODY|NAME
&id1=1311
&var_name1=request%5Bper_page%5D

,client: IPアドレス, server: ドメイン, request: “POST /wp-admin/admin-ajax.php HTTP/2.0”, host: “ドメイン”, referrer: “https://ドメイン/wp-admin/theme-install.php?browse=featured”

zoneN(Nは任意の数字)に”BODY|NAME”という文字列がありますが、これはルールがマッチしたゾーンを指しています。公式ドキュメントが分かりにくいのですが、”BODY”はPOSTの引数、”NAME”は変数の名前を意味するようです。

idN(Nは任意の数字)に”1310″と”1311″という数字がありますが、これはNAXSIでマッチした検知ルールのIDを示しています。設定ファイルで1310および1311を確認すると、半角鍵括弧に関わるルールである事が分かりました。

var_nameN(Nは任意の数字)に”request%5Bper_page%5D”という表記がありますが、一部エンコードされている部分をデコードすると”request[per_page]”になります。

2.ルールの作成

先述した内容を踏まえ、他の除外ルールも参考にしつつルールを作成すると、下記のようになりました。

BasicRule wl:1310,1311 “mz:$URL:/wp-admin/admin-ajax.php|$BODY_VAR_X:request\[[-_a-zA-Z0-9]+\]|NAME”;

これは以下の条件にマッチします。

  • NAXSI検知ルールの1310,1311に一致
  • URLの/wp-admin/admin-ajax.phpに一致
  • 変数名のrequest[半角英数と”-“と”_”で構成される任意の文字列]に一致

(”-“は変数名に使えないので要らないですね…)

3.ルールの追加

今回作成したルールを、除外ルール設定ファイルに追記します。

追記したらNginxを再起動しておきましょう。

KUSANAGIのWAF(NAXSI)初期状態に対する個人的見解

自分でルールを一切追加していない状態では、先述したテーマ追加画面におけるエラーの他に、ウィジェットの配置や保存が出来ない問題も発生します。

これらは、利用頻度は低いといえどWordPressの標準機能であるのに、何故最初からWAFの除外ルールが設けられていないのかを考えました。

恐らくこれは、コンテンツの表示や検索・投稿の追加、WordPress本体の自動更新といった、通常の運営に支障が出ない程度の最低限のWAF除外ルールを設ける事によって、セキュリティを高めているのだと思います。

小さな個人ブログ程度ではあまりやらないでしょうが、制作や改修といった作業はアクセスが制限された開発環境やテスト環境で行い、それらの作業が全て完了した後に、WAFで守られて一般公開されている本番環境へリリースする(反映させる)、という管理フローもあります。この場合、作業する環境での作業中は常にWAFをOFFにしておけば良いので、除外ルールを追加する必要もありません。

なお、常に大量のアクセスがあるサイトでも無く、即座に完了させられる程度の作業であれば、WAFをOFFにして直ぐにONに戻す方が、一時的にルールを追加するよりも労力的に楽でしょう。

最後に

WAFがあるのなら使わない手はありませんが、WAF無効時は問題が無かった操作が遮断される事は高い確率で起こり得るます。適切な対処方法を持ってWAFと付き合っていく必要があります。