[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) 時でも、同様の動作をさせることが出来ます。

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