Nginx + CrowdSec でセキュリティを強固にしてみた

2026/06/14
以前、Next.js の脆弱性を突いた攻撃を受けてサーバーをまるごと潰したことがありました。それ以来アクセスログをちょくちょく眺めるようになったんですが、見てみると脆弱性を狙ったと思しき不審なリクエストが結構来ていたので、さすがにちゃんとした対策を入れようと思い立ちました。
今回導入したのが CrowdSec です。Nginx と組み合わせて不審なアクセスを自動ブロックできるようにしたので、手順をまとめておきます。
CrowdSec とは
CrowdSec はオープンソースのセキュリティエンジンで、ログを解析してブルートフォースやスキャンなどの不審な挙動を検知・ブロックしてくれるやつです。
fail2ban に近い立ち位置ですが、クラウド上でシグナルを共有するコミュニティ型なのが特徴で、世界中のユーザーが検知した悪意あるIPをブロックリストとして活用できます。
前提
Nginx はインストール済み
Ubuntu 系のディストリビューションを想定
インストール手順
1. CrowdSec リポジトリの追加 & インストール
curl -s https://install.crowdsec.net | sudo bash
sudo apt update
sudo apt install crowdsec
公式スクリプトでリポジトリを登録してからインストールするだけです。
2. Nginx Bouncer の依存パッケージをインストール
sudo apt install lua5.1 libnginx-mod-http-lua luarocks gettext-base lua-cjson
Nginx 向けの Bouncer は Lua で動くので、Lua 関連のパッケージが必要です。
3. CrowdSec Nginx Bouncer のインストール
sudo apt install crowdsec-nginx-bouncer
これで CrowdSec が検知した不審なIPを、Nginx レベルで自動ブロックできるようになります。
動作確認
インストール後、一応ちゃんと動いてるか確認しておきます。
# 検知シナリオの一覧を確認
sudo cscli scenarios list
# 現在のブロックリスト(decisions)を確認
sudo cscli decisions list
実際に sudo cscli decisions list を実行するとこんな感じで表示されます。
╭────┬──────────┬───────────────────┬───────────────────────────────────────┬────────┬─────────┬──────────────────────────────────────────────────┬────────┬────────────┬──────────╮
│ ID │ Source │ Scope:Value │ Reason │ Action │ Country │ AS │ Events │ expiration │ Alert ID │
├────┼──────────┼───────────────────┼───────────────────────────────────────┼────────┼─────────┼──────────────────────────────────────────────────┼────────┼────────────┼──────────┤
│ 8 │ crowdsec │ Ip:117.177.102.79 │ crowdsecurity/thinkphp-cve-2018-20062 │ ban │ CN │ 9808 China Mobile Communications Group Co., Ltd. │ 1 │ 3h8m3s │ 9 │
│ 3 │ crowdsec │ Ip:66.132.172.139 │ crowdsecurity/http-bad-user-agent │ ban │ US │ 398324 CENSYS-ARIN-01 │ 2 │ 2h1m31s │ 3 │
│ 2 │ crowdsec │ Ip:20.220.150.55 │ crowdsecurity/http-crawl-non_statics │ ban │ CA │ 8075 MICROSOFT-CORP-MSN-AS-BLOCK │ 92 │ 1h52m38s │ 2 │
│ 1 │ crowdsec │ Ip:51.68.111.216 │ crowdsecurity/http-bad-user-agent │ ban │ FR │ 16276 OVH SAS │ 2 │ 1h43m1s │ 1 │
╰────┴──────────┴───────────────────┴───────────────────────────────────────┴────────┴─────────┴──────────────────────────────────────────────────┴────────┴────────────┴──────────╯
Reason 列に検知したシナリオ名が入っていて、CVE 番号がそのまま出てきたりします。thinkphp-cve-2018-20062 のように、何の攻撃を防いだかが一目でわかります。
CVE の攻撃もブロックしてくれるのが嬉しい
個人的に一番嬉しかったのが、CVE として報告されている既知の攻撃パターンも自動でブロックしてくれる点です。
CrowdSec のコミュニティシナリオには CVE に対応した検知ルールも含まれていて、自分では把握しきれないような攻撃もログに記録・ブロックしてくれます。冒頭の Next.js の件みたいに「やられてから気づく」じゃなく事前に防げるのは本当に安心感が違います。個人運用の小さなサーバーでも、こういうレイヤーが一枚あるだけでだいぶ違いますね。
ハマりポイント:普通に使ってるだけで 403 が出る
CrowdSec を有効にしてしばらく経つと、普通にアプリを使っているだけで 403 が頻発するという事態が発生しました。
原因を調べると、crowdsecurity/http-crawl-non_statics というシナリオが発動していました。「静的ファイル以外を短時間に大量リクエストしている挙動」を検知するシナリオです。
自分のウェブアプリ、画面を操作するたびに複数の API リクエストが同時に走る作りになっていて、それが引っかかっていました。
当面の対処として該当シナリオをシミュレーションモードに切り替えました。まあ、そもそもブロックされるような大量のリクエストを投げる作りがいけない気がしますけどね笑。シミュレーションモードでは検知はするけど実際にはブロックしないので、ログを見ながら状況を把握できます。
sudo cscli simulation enable crowdsecurity/http-crawl-non_statics
sudo systemctl restart crowdsec
これで解消されました。
シミュレーションモードの使い分け
モード 挙動 通常モード 検知したらブロック シミュレーションモード 検知してもブロックしない(ログのみ)
完全に無効化するんじゃなくシミュレーションモードにしておくのがポイントで、ログには残るので将来「やっぱりブロックしよう」となったときにすぐ戻せます。
確認・解除は以下で。
# シミュレーション中のシナリオ一覧
sudo cscli simulation list
# シミュレーションモードを解除したいとき
sudo cscli simulation disable crowdsecurity/http-crawl-non_statics
sudo systemctl restart crowdsec
まとめ
導入自体はスクリプト一発+数コマンドで完結するので思ったより手軽でした。CVE ベースの攻撃もカバーしてくれるので、過去にやられた経験がある身としては「もっと早く入れておけばよかった」というのが正直なところです。
ただ自分のアプリの挙動によってはシナリオが誤検知することもあります。その場合は sudo cscli decisions list でブロックされた Reason を確認して、該当シナリオをシミュレーションモードにするといった対応が取れます。今回の http-crawl-non_statics のように、アプリ側の設計に起因しているケースもあるので、ログを見ながら原因を整理するのが早いです。