[Apache] “Basic認証に失敗したら、自動でアクセスを拒否るスクリプト”を作ってみた

[Apache] “Basic認証に失敗したら、自動でアクセスを拒否るスクリプト”を作ってみた はてなブックマーク - [Apache] “Basic認証に失敗したら、自動でアクセスを拒否るスクリプト”を作ってみた


◆ Basic 認証に失敗したら自動で拒否る → ウェブのセキュリティうp!→ (^p^)

今回もメモです。ちょっと会社で仕事中、同僚と「あったら良いかもね」という話になり作ってみたのがこれ。Apache の Basic 認証に失敗すると、自動的にアクセスを拒否する Perl スクリプトをつくってみました。

動作原理はシンプルです。 .htaccess の「deny from xxxx」という記述を自動生成する CGI を作成しました。具体的なステップは、以下の通りです。

HTTP コード 401 (Unauthorized) 時に指定の CGI を動作
↓
IP アドレスをファイルに記録する
↓
記録された IP アドレスが既定回数を超える
↓
.htaccess でホストを拒否る(deny from に追加する)

という単純なものです。

試作したスクリプトでは、連続x回アクセスすると拒否するようにしてみました(xは任意)。

動作確認の出来る URL は http://pocketstudio.jp/auth/ です。ここでは、3回連続してパスワードを間違えると、次回以降は自動でアクセスを拒否し、認証画面すら表示させません。

以下、設置手順です。

◆動作条件

動作の前提として

  • .htaccess がサーバで利用可能なこと(AllowOverride All)
  • ウェブサーバが Apache で、Basic 認証が使えること(Options All)
  • 拡張子 .cgi のファイルを CGI (Perlスクリプト) として動作できること

が必要になります。

◆ 設置

まず、URL は「 http://example.jp/auth/ 」、サーバ内のパスは「 /var/www/html/auth/ 」と仮定して進めます。

サーバの /var/www/html/auth/ ディレクトリ内には、以下のファイルを設置します。

  • .htaccess ( パーミッション rw—-rw- [606] )
    ErrorDocument 401 /autodeny.cgi
    
    AuthType        Basic
    AuthName        "Authentication required"
    AuthUserFile    /var/www/html/auth/.htpasswd
    AuthGroupFile   /dev/null
    require         valid-user
    
    Order Allow,Deny
    Allow from all
  • .htlist ( パーミッション rw—-rw- [606])
    認証に失敗したIPアドレスと回数を記録するものです。設置時は中身はカラッポで大丈夫です。(ファイル名を .htlist にしたのは、Apache のデフォルト設定で .ht* の付くファイル名は非表示に設定されているからです。非表示設定がなければ、このファイルはドキュメントルートとは違う階層にファイルを置くべきです。)

  • .htpasswd (rw—-r– [604])
    認証用ファイルです。作成は、htpasswd コマンドを使います。

    $ htpasswd -c /var/www/html/auth/.htpasswd ユーザ名
    New password:    (画面に表示されませんが、パスワードを入力)
    Re-type new password:   (パスワードを再入力)
    Adding password for user ユーザ名

次は、拒否用のスクリプト http://URL/deny.cgi を /var/www/html/autodeny.cgi に設置します。パーミッションは rwx—r-x [705] にしておきます。以下 autodeny.cgi の内容です。

#!/usr/bin/perl

# autodeny.cgi ver 0.1
#
# Masahito Zembutsu
# http://pocketstudio.jp/

$htaccess = '/var/www/html/auth/.htaccess';
$htlist   = '/var/www/html/auth/.htlist';
$denyhost = $ENV{'REMOTE_ADDR'};
$COUNT    = '3'; # 指定した回数連続して認証失敗すると、拒否する

if ($denyhost) {
        my ($ip,$hit);
        if (open(HOST,$htlist)) {
                my $host = <HOST>;
                ($ip,$hit) = split(/\t/, $host);
                close(HOST);
                if ($ip ne $denyhost) {
                        $ip = $denyhost;
                        $hit = 1;
                } else {
                        $hit++;
                }
                &denyHost($ip) if ($hit >= $COUNT);
        }
        if (open(HOST,">$htlist")) {
                print HOST "$ip\t$hit";
                close(HOST);
        }
}

print "Content-type: text/plain\n\n";
print "Access Denied"; # 認証失敗後に表示されるメッセージ
exit;

sub denyHost {

        if(open (HT,">>$htaccess")) {
                print HT "deny from $_[0]\n";
        }
        return;
}

1;

あとは、ブラウザから http://URL/auth/ にアクセスします。3回連続してパスワード入力に失敗すると「Forbidden」に切り替わり、二度と認証できなくなります。

なお、再び認証できるようにするためには、.htaccess ファイルを開き、「deny from <IP Address>」の行を削除してください。

※ 例として HTTP status が 401 (Unauthorized) の時に適用されるようにしましたが、403 (forbiddon) や 404 (File not found) 時でも、同様の動作をさせることが出来ます。

設置・動作等でよく分からなければ、コメント欄等でお気軽にお訊ねください(・∀・)