意地でもBASIC認証を使いたいあなたへ@SSL化&多要素認証&Fail2Banでお手軽要塞
前提
・Azure仮想マシンを利用する
・firewalld/SELinuxは無効にしている
・OSはRHEL9.5/Gen2
・Apache/2.4.62
・今回はGoogleAuthenticatorを使う
・自己証明書を使う
・コマンド実行はすべてroot権限で行っています
用語のおさらい
BASIC認証とは?
ウェブサーバーが提供する最もシンプルな認証方式の一つです。ユーザー名とパスワードを入力することで、特定のページやディレクトリへのアクセスを制限できます。
Fail2Banとは?
不正なログイン試行を検出し、自動的にIPアドレスをブロックするセキュリティツールです。主にSSHやApache、nginxなどのサービスを保護するために使用されます。
TOTP(Time-based One-time Password)とは
一定時間ごとに変わるワンタイムパスワードを生成する、多要素認証の一つです。
今回はGoogle Authenticatorを使います。※Microsoftのでもいい。
※多要素認証は2つ以上の要素を組み合わせて認証を行う仕組みです。
単純なパスワード認証よりも高いセキュリティを提供します。
今回で言うとサーバー上に格納してあるユーザという要素(知識要素)とアプリとかで発行されるトークンという要素(所持要素)のこと。
実装方法(手順を進めるごとに要塞になります)
①前準備 (ノーガード)
firewalld無効 (任意)
systemctl stop firewalld
systemctl disable firewalld
systemctl mask firewalld
SELINUX無効 (任意)
sed -i 's/^SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config
setenforce 0
reboot
補足
vi で /etc/selinux/config を直接編集してもOK。
setenforce 0 は即座に Permissive になるが、Disabled にはならない。
Permissive の状態では、SELinuxのアクセス制御は行われないが、ログには記録される。完全に Disabled にするにはrebootが必要。
rebootしなくても Permissive の状態ならブロックされないので、動作的には問題ない。
必要なパッケージ系のインストールと起動設定

ブラウザから見えるように設定
#現在設定がしてあるドキュメントルートにhtmlファイルを置く
grep -i "DocumentRoot" /etc/httpd/conf/httpd.conf |grep -v "#"
DocumentRoot "/var/www/html"
#ファイルの新規作成 ※「>」は上書き「>>」は追記。。。苦い思い出があるので一応追記にしてます
echo "Apache Test" >> /var/www/html/index.html
#権限設定
chown apache:apache /var/www/html/index.html
#必要な場合
chmod 644 /var/www/html/index.html
これでブラウザでサーバーIPをたたいたらテストページが見えるハズ!
②自己証明書を使ったSSL化 (一般的なセキュア)
下準備
#opensslが使えるかどうか確認する
openssl version
OpenSSL 3.2.2 4 Jun 2024 (Library: OpenSSL 3.2.2 4 Jun 2024)
#なければインストール
dnf install -y openssl
#sslに必要なモジュールをインストール
dnf install -y mod_ssl
※mod_sslをインストールすると以下のディレクトリができる
/etc/httpd/conf.d/ssl.conf
自己証明書(鍵)の作成
#鍵を作る場所の移動(後から移動する)
cd ~
# 以下コマンドは 証明書の対(server.key server.crt)作成と有効期限365日の指定です。
openssl req -x509 -newkey rsa:4096 -keyout server.key -out server.crt -days 365 -nodes
#以下は
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Country Name (2 letter code) [XX]:JP
State or Province Name (full name) []:TOKYO
Locality Name (eg, city) [Default City]:hoge
Organization Name (eg, company) [Default Company Ltd]:huga
Organizational Unit Name (eg, section) []:piyo
Common Name (eg, your name or your server's hostname) []:hogehugapiyo.jp
Email Address []:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#確認(抜粋) 2つ作成されていること
ls -lsa
4 -rw-r--r-- 1 root root 2025 Feb 26 08:04 server.crt
4 -rw------- 1 root root 3272 Feb 26 08:03 server.key
作った自己証明書をApacheに読み込ませる設定
#sslの設定ファイルの存在確認(mod_sslインストール時に配置されるハズ。。)新規作成でも可
ls -lsa /etc/httpd/conf.d/ssl.conf
#ssl設定ファイルがincludeされていることを確認(IncludeOptional conf.d/*.conf)
cat /etc/httpd/conf/httpd.conf |grep "Include" |grep -v "#"
Include conf.modules.d/*.conf
IncludeOptional conf.d/*.conf ←これ
#鍵の配置場所を確認
grep "SSLCertificate" /etc/httpd/conf.d/ssl.conf | grep -v "#"
SSLCertificateFile /etc/pki/tls/certs/localhost.crt
SSLCertificateKeyFile /etc/pki/tls/private/localhost.key
#名前がlocalhostなので変更しつつ移動する
mv /root/server.crt /etc/pki/tls/certs/localhost.crt
mv /root/server.key /etc/pki/tls/private/localhost.key
httpの通信をhttpsにリダイレクトさせる設定

今回は自己証明書の為、見た目や信頼性の部分は良くないが、暗号化はされるのでテスト環境にはいいと思う。

③多要素認証の導入 (もうだいぶ要塞)
多要素認証のユーザを作るスクリプトを作成&実行
#スクリプトを作る場所(どこでもいい)
cd ~
#スクリプト作成「.shがついてれば名前は何でもいい」
vim makeauth-users.sh
#=====================
#!/bin/bash
user=${1:?Usage: $0 username}
file="/etc/httpd/conf/otp-users"
secret=$(cat /dev/urandom | tr -dc 'a-z0-9' | fold -w 15 | head -n 1)
secret_16=$(python3 -c "import base64; print(base64.b16encode(b'$secret').decode())")
secret_32=$(python3 -c "import base64; print(base64.b32encode(b'$secret').decode().rstrip('='))")
otpauth_uri="otpauth://totp/${issuer}:${user}?secret=${secret_base32}&issuer=${issuer}"
# ファイルがなければ作成
[ -f "$file" ] || touch "$file"
# 既存ユーザー確認して追加
if grep -q "^HOTP/T30 $user" "$file"; then
echo " '$user' は既に存在します."
else
echo "HOTP/T30 $(printf '%-12s' $user) - $secret_16" >> "$file"
echo "'$user' を追加しました。 アプリに設定する鍵は: $secret_32 です"
fi
#=====================
#権限を追加
chmod +x makeauth-users.sh
#多要素認証のユーザ(makino)を作成(secretの値はアプリで設定します)
./makeauth-users.sh makino
'makino' を追加しました。 アプリに設定する鍵は: XXXXXXXXXXXXXXXXXXXXXXX です
#ユーザ確認
cat /etc/httpd/conf/otp-users
#HOTP/T30 makino - YYYYYYYYYYYYYYYYYYYYYYYYY
アプリ側設定
GoogleAuthenticatorアプリを開く。
「+」を押し、セットアップキーを入力をクリック
以下の情報を入力
「アカウント名」分かり易ければなんでもいい
(例 makino@webServer)
「鍵」
ユーザを作った時のsecretの値(XXXXXXXXXXXXXXXXXXXXXXX)
「鍵の種類」
時間ベース
BASIC認証の設定
#設定ファイル修正 注意:Directory内は修正し、モジュールは新規追加
vim /etc/httpd/conf/httpd.conf
==============================================================================
LoadModule authn_otp_module /usr/lib64/httpd/modules/mod_authn_otp.so
<Directory "/var/www/html">
AuthType Basic
AuthName "ワンタイムパスワード認証が必要です"
AuthBasicProvider OTP
Require valid-user
OTPAuthUsersFile /etc/httpd/conf/otp-users
OTPAuthMaxLinger 3600
OTPAuthMaxOTPFailure 20
OTPAuthPINAuthProvider file
OTPAuthLogoutOnIPChange On
</Directory>
==============================================================================
※モジュールの追加は忘れないこと。
※今回はドキュメントルートに設定しています。
※設定ファイルを更新したら反映させること
※反映(再起動またはリロード)
systemctl restart httpd
systemctl reload httpd
これでブラウザでサーバーIPをたたいたらBASIC認証が聞かれるハズ!

ユーザ名はmakeauth-users.shで作ったもの
パスワードはGoogleAuthenticatorで表示される6桁の数字
Apacheに多要素認証を導入する方法については以上です。
④Fail2Banの導入 (要塞)
設定ファイルの作成と編集
Fail2Ban の設定は基本的に /etc/fail2ban/jail.local に行います。jail.conf はデフォルト設定なので直接編集せず、jail.local に必要な設定を上書きする形で。
# 設定ファイルをコピーして作成
cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
Apache 向け設定の追加
以下の設定を jail.local に追加します。
#設定ファイル修正
vim /etc/fail2ban/jail.local
[apache-otp]
enabled = true
port = http,https
filter = apache-auth
logpath = /var/log/httpd/error_log
maxretry = 5
bantime = 24h
findtime = 600
補足
enabled: このルールを有効化
port: 監視するポート(HTTP と HTTPS)
filter: 初期フィルタ(apache-authはfail2ban/filter.d/内にinstallされる)
logpath: ログファイルのパス(Apache のエラーログ)
maxretry: 指定時間内に許容する最大失敗回数
bantime: ブロックする秒数
findtime: 試行失敗回数をカウントする時間(秒)
Fail2Ban の再起動と確認
#Fail2Ban再起動(反映)
systemctl restart fail2ban
fail2ban-client reload
#確認 ※Jailリストにapache-otpがある事
fail2ban-client status
Status
|- Number of jail: 1
`- Jail list: apache-otp
#現在ブロックされているIP達
fail2ban-client status apache-otp
Status for the jail: apache-otp
|- Filter
| |- Currently failed: 0
| |- Total failed: 20
| `- File list: /var/log/httpd/error_log
`- Actions
|- Currently banned: 1
|- Total banned: 1
`- Banned IP list: xxx.xxx.xxx.xxx
#特定の IP をブロック解除(1が出たら解除完了)
fail2ban-client set apache-otp unbanip xxx.xxx.xxx.xxx
1
設定は以上!!
お疲れ様でした。
まとめ
今回の要件としてはBASIC認証を絶対使わないといけない。
だけどセキュリティはカチカチにしたい。という方向けの記事になります。
BASIC認証の攻撃リスクについてよくあげられるのが以下3点です。
1.通信の盗聴(平文送信) ※中間者攻撃
2.総当たり攻撃に弱い ※ブルートフォース攻撃
3.パスワード漏洩 ※リスト型攻撃
今回はそれぞれの攻撃に対応する設定方法を記載しました。
1.証明書を使いSSL化することで通信が平文で送信されることの防止。
自己証明書ではなくValuessl等で正式な証明書であれば見た目・信頼性共に良い。ただし料金はかかるので自己証明書でも可。
2.File2Banを使い、同じIPからの総当たり攻撃を防止する。
カスタマイズもでき、要件にあった設定が可能。
3.TOTP認証を使うので万が一パスワードを盗まれても数秒で新しく発行がされるので安心。(SSL化すればさらに安心)ユーザーごとにシークレットキーを作成することができる。
少しでもみなさまのお役に立てれば幸いです。以上。