序章

ModSecurityは、Apache、Nginx、IISで動作する無料のWebアプリケーションファイアウォール(WAF)です。 シンプルで複雑な操作を実行するための柔軟なルールエンジンをサポートし、SQLインジェクション、クロスサイトスクリプティング、トロイの木馬、悪意のあるユーザーエージェント、セッションハイジャック、その他多くのエクスプロイトのルールを含むコアルールセット(CRS)が付属しています。 Apacheの場合、インストールと構成を簡単にする追加モジュールとしてロードされます。

前提条件

このチュートリアルに従うには、次のものが必要です。

  • Ubuntu14.04またはDebian8ドロップレット。

  • Ubuntu14.04またはDebian8の初期サーバーセットアップチュートリアルに従ってセットアップできるsudo特権を持つ標準ユーザーアカウント。

  • Ubuntu14.04またはDebian8のチュートリアルに従ってインストールできるLAMPスタック。

ステップ1—ModSecurityのインストール

このステップでは、ModSecurityをインストールします。

まず、パッケージインデックスファイルを更新します。

  1. sudo apt-get update

次に、ModSecurityをインストールします。

  1. sudo apt-get install libapache2-mod-security2 -y

次のコマンドを使用して、ModSecurityモジュールがロードされたことを確認できます。

  1. sudo apachectl -M | grep --color security2

出力がsecurity2_module (shared)の場合、これはモジュールがロードされたことを示します。

ModSecurityのインストールには、名前を変更する必要のある推奨構成ファイルが含まれています。

  1. sudo mv /etc/modsecurity/modsecurity.conf-recommended /etc/modsecurity/modsecurity.conf

最後に、Apacheをリロードします。

  1. sudo service apache2 reload

ModSecurityの新しいログファイルが/var/log/apache2/modsec_audit.logのApacheログディレクトリに作成されます。

ステップ2—ModSecurityの構成

箱から出して、ModSecurityはルールが機能する必要があるため、何もしません。 このステップでは、最初にいくつかの構成ディレクティブを有効にします。

このステップで構成ディレクティブを見つけて置き換えるために、ストリームエディターであるsedを使用します。 このツールの詳細については、sedチュートリアルシリーズをお読みください。

有効にする基本的なディレクティブ

デフォルトのModSecurity構成ファイルはDetectionOnlyに設定されており、ルールの一致に従って要求をログに記録し、何もブロックしません。 これは、modsecurity.confファイルを編集し、SecRuleEngineディレクティブを変更することで変更できます。 実稼働サーバーでこれを試している場合は、すべてのルールをテストした後でのみ、このディレクティブを変更してください。

  1. sudo sed -i "s/SecRuleEngine DetectionOnly/SecRuleEngine On/" /etc/modsecurity/modsecurity.conf

SecResponseBodyAccessディレクティブは、応答本文をバッファリングするかどうかを構成します(つまり、 ModSecurityによって読み取られます)。 これは、データ漏洩の検出と保護が必要な場合にのみ必要です。 したがって、オンのままにすると、Dropletリソースが使い果たされ、ログファイルのサイズも大きくなるため、オフにします。

  1. sudo sed -i "s/SecResponseBodyAccess On/SecResponseBodyAccess Off/" /etc/modsecurity/modsecurity.conf

変更するオプションのディレクティブ

/etc/modsecurity/modsecurity.confを編集してカスタマイズできるディレクティブは他にもあります。 SecRequestBodyLimitおよびSecRequestBodyNoFilesLimitディレクティブは、Webアプリケーションに投稿できる最大データを制限します。

特に、SecRequestBodyLimitディレクティブは最大POSTデータサイズを指定します。 クライアントからそれより大きいものが送信された場合、サーバーは 413 Request Entity TooLargeエラーで応答します。 Webアプリケーションにファイルのアップロードがない場合は、この値をそのままにしておくことができます。 構成ファイルで指定されている事前構成値は13107200バイト(12.5MB)です。 この値を変更する場合は、次の行modsecurity.confを探してください。

オプションの`modsecurity.conf`ディレクティブの変更
SecRequestBodyLimit 13107200

同様に、SecRequestBodyNoFilesLimitは、POSTデータからファイルのアップロードを差し引いたサイズを制限します。 この値は、誰かが非常に大きなサイズのリクエストボディを送信しているときに、サービス拒否(DoS)攻撃を受けやすくするために、できるだけ低く設定する必要があります。 構成ファイルの事前構成値は131072バイト(128KB)です。 この値を変更する場合は、次の行modsecurity.confを探してください。

オプションの`modsecurity.conf`ディレクティブの変更
SecRequestBodyNoFilesLimit 131072

サーバーのパフォーマンスに影響を与えるディレクティブはSecRequestBodyInMemoryLimitです。 このディレクティブはほとんど自明です。 これは、メモリ(RAM)に保持する「要求本文」データ(POSTデータ)の量を指定します。それ以上のものは(スワッピングのように)ハードディスクに配置されます。 ドロップレットはSSDを使用するため、これはそれほど問題にはなりません。 ただし、RAMに余裕がある場合は、これを変更できます。 このディレクティブの事前構成された値は128KBです。 この値を変更する場合は、次の行modsecurity.confを探してください。

オプションの`modsecurity.conf`ディレクティブの変更
SecRequestBodyInMemoryLimit 131072

ステップ3—SQLインジェクションのテスト

いくつかのルールを構成する前に、ModSecurityの保護をテストするためにSQLインジェクションに対して脆弱なPHPスクリプトを作成します。

注:これは、セッション処理やフォームサニテーションのない基本的なPHPログインスクリプトです。 これは、SQLインジェクションとModSecurityのルールをテストするための例として使用されています。 チュートリアルが終了する前に削除されます。

まず、MySQLプロンプトにアクセスします。

  1. mysql -u root -p

ここで、 sample というMySQLデータベースを作成し、それに接続します。

  1. create database sample;
  2. connect sample;

次に、いくつかの資格情報(ユーザー名 sammyとパスワードパスワード)を含むテーブルを作成します。

  1. create table users(username VARCHAR(100),password VARCHAR(100));
  2. insert into users values('sammy','password');

最後に、MySQLプロンプトを終了します。

  1. quit;

次に、Apacheのドキュメントルートにログインスクリプトを作成します。

  1. sudo nano /var/www/html/login.php

次のPHPスクリプトをファイルに貼り付けます。 スクリプトがデータベースに接続できるように、以下のスクリプトのMySQLパスワードを前に設定したものに変更してください。

/var/www/html/login.php

<html>
<body>
<?php
    if(isset($_POST['login']))
    {
        $username = $_POST['username'];
        $password = $_POST['password'];
        $con = mysqli_connect('localhost','root','your_mysql_password','sample');
        $result = mysqli_query($con, "SELECT * FROM `users` WHERE username='$username' AND password='$password'");
        if(mysqli_num_rows($result) == 0)
            echo 'Invalid username or password';
        else
            echo '<h1>Logged in</h1><p>This is text that should only be displayed when logged in with valid credentials.</p>';
    }
    else
    {
?>
        <form action="" method="post">
            Username: <input type="text" name="username"/><br />
            Password: <input type="password" name="password"/><br />
            <input type="submit" name="login" value="Login"/>
        </form>
<?php
    }
?>
</body>
</html>

このスクリプトはログインフォームを表示します。 ブラウザを開き、http://your_server_ip/login.phpに移動して表示します。 正しいクレデンシャルのペアを入力した場合、たとえば ユーザー名フィールドにsammy、 Password フィールドにパスワード、メッセージが表示されます。これは、有効な資格情報でログインした場合にのみ表示されるテキストです。 ログイン画面に戻って間違ったクレデンシャルを使用すると、無効なユーザー名またはパスワードというメッセージが表示されます。

次の仕事は、ログインページをバイパスするためにSQLインジェクションを試すことです。 ユーザー名フィールドに次のように入力します。

SQLインジェクションのユーザー名
' or true -- 

このインジェクションが機能するには、-- の後にスペースが必要であることに注意してください。 パスワードフィールドを空のままにして、ログインボタンを押します。 スクリプトは、認証されたユーザー向けのメッセージを表示します。 次のステップでは、これを防ぎます。

ステップ4—ルールを設定する

このステップでは、いくつかのModSecurityルールを設定します。

CRSの有効化

物事を簡単にするために、ModSecurityと一緒にすでにインストールされている多くのルールがあります。 これらはCRS(コアルールセット)と呼ばれ、/usr/share/modsecurity-crsディレクトリにあります。 これらのルールをロードするには、これらのディレクトリ内の.confファイルを読み取るようにApacheを設定する必要があるため、security2.confファイルを開いて編集します。

sudo nano /etc/apache2/mods-enabled/security2.conf

ファイルの最後の行(</IfModule>)の前に、赤で強調表示されている次の2つのディレクティブを追加します。

security2.confを更新しました
        IncludeOptional /etc/modsecurity/*.conf
        IncludeOptional "/usr/share/modsecurity-crs/*.conf"
        IncludeOptional "/usr/share/modsecurity-crs/activated_rules/*.conf"
</IfModule>

ファイルを保存して閉じます。

ディレクトリ/ドメインを除く(オプション)

ModSecurityはSQLクエリをブロックするため、phpMyAdminなどのアプリケーションを実行している場合は、特定のディレクトリまたはドメイン名を除外することが理にかなっている場合があります。 WordPressのようなCMSアプリケーションの管理バックエンドを除外することもお勧めします。 新しいサーバーでこのチュートリアルを実行している場合は、この手順をスキップできます。

完全なVirtualHostのModSecurityを無効にするには、仮想ホストファイルの<VirtualHost>[...]</VirtualHost>ブロック内に次のディレクティブを配置します。

<IfModule security2_module>
    SecRuleEngine Off
</IfModule>

特定のディレクトリを省略する場合(たとえば、/var/www/wp-admin):

<Directory "/var/www/wp-admin">
    <IfModule security2_module>
        SecRuleEngine Off
    </IfModule>
</Directory>

ディレクトリでModSecurityを完全に無効にしたくない場合は、SecRuleRemoveByIdディレクティブを使用して、IDを指定して特定のルールまたはルールチェーンを削除します。

<LocationMatch "/wp-admin/update.php">
    <IfModule security2_module>
        SecRuleRemoveById 981173
    </IfModule>
</LocationMatch>

SQLインジェクションルールのアクティブ化

次に、SQLインジェクションルールファイルをアクティブ化します。 必要なルールファイルは、Apacheのmods-enabledディレクトリと同様のactivated_rulesディレクトリにシンボリックリンクされている必要があります。 activated_rulesディレクトリに移動します。

  1. cd /usr/share/modsecurity-crs/activated_rules/

次に、modsecurity_crs_41_sql_injection_attacks.confファイルからシンボリックリンクを作成します。

  1. sudo ln -s ../base_rules/modsecurity_crs_41_sql_injection_attacks.conf .

最後に、ルールを有効にするためにApacheをリロードします。

  1. sudo service apache2 reload

次に、前に作成したログインページを開き、ユーザー名フィールドで同じSQLインジェクションクエリを使用してみます。 手順2でSecRuleEngineディレクティブをOnに変更したため、 403Forbiddenエラーが表示されます。 (SecRuleEngineDetectionOnlyオプションのままになっている場合、インジェクションは成功しますが、試行はmodsec_audit.logファイルに記録されます。)

このPHPログインスクリプトはModSecurityのテストのみを目的としているため、テストが完了したら削除する必要があります。

  1. sudo rm /var/www/html/login.php

ステップ5—独自のルールを作成する

このセクションでは、スパムに一般的に関連付けられている特定の単語がHTMLフォームに入力された場合に、リクエストをブロックするルールチェーンを作成します。

最初に、テキストボックスから入力を取得してユーザーに表示するサンプルPHPスクリプトを作成します。 form.phpというファイルを開いて編集します。

  1. sudo nano /var/www/html/form.php

次のコードを貼り付けます。

/var/www/html/form.php
<html>
    <body>
        <?php
            if(isset($_POST['data']))
                echo $_POST['data'];
            else
            {
        ?>
                <form method="post" action="">
                        Enter something here:<textarea name="data"></textarea>
                        <input type="submit"/>
                </form>
        <?php
            }
        ?>
    </body>
</html>

カスタムルールは、任意の構成ファイルに追加したり、ModSecurityディレクトリに配置したりできます。 ルールをmodsecurity_custom_rules.confという別の新しいファイルに配置します。

sudo nano /etc/modsecurity/modsecurity_custom_rules.conf

このファイルに以下を貼り付けます。 ブロックしている2つの単語は、blockedword1blockedword2です。

modsecurity_custom_rules.conf
SecRule REQUEST_FILENAME "form.php" "id:'400001',chain,deny,log,msg:'Spam detected'"
SecRule REQUEST_METHOD "POST" chain
SecRule REQUEST_BODY "@rx (?i:(blockedword1|blockedword2))"

SecRuleの構文はSecRule VARIABLES OPERATOR [ACTIONS]です。 ここでは、チェーンアクションを使用して、変数REQUEST_FILENAMEform.phpと、REQUEST_METHODPOSTと、REQUEST_BODYを正規表現(@rx)文字列(blockedword1|blockedword2)?i:は、大文字と小文字を区別しない一致を行います。 これら3つのルールすべてが正常に一致すると、ACTIONは拒否し、msg "Spam detected."でログに記録します。チェーンアクションは、論理積をシミュレートして3つのルールすべてに一致します。

ファイルを保存し、Apacheをリロードします。

sudo service apache2 reload

ブラウザでhttp://your_server_ip/form.phpを開きます。 blockedword1またはblockedword2を含むテキストを入力すると、403ページが表示されます。

このPHPフォームスクリプトはModSecurityのテストのみを目的としているため、テストが完了したら削除する必要があります。

  1. sudo rm /var/www/html/form.php

結論

このチュートリアルでは、ModSecurityをインストールして構成し、カスタムルールを追加する方法を学習しました。 詳細については、公式のModSecurityドキュメントをご覧ください。