🕳️

Cloudflare入れたのに75%素通りだった話 ー 攻撃者も学習する

に公開
4

📝 続編あります: iptables入れたのにまだ27%直接アクセスされてた ー IPv6を忘れていた(https://zenn.dev/dhc4aki/articles/955d4fcbea3196)

Cloudflare入れて安心してた

先日、WordPressサイトにCloudflareを導入しました。

https://zenn.dev/dhc4aki/articles/cloudflare-free-496-blocks

24時間で496件の脅威を緩和。エラー率も45%→32%に改善。「無料でここまでできるのか」と感動してました。

DNS浸透すれば完璧だろう、と。

「あれ、緩和率が下がってる...?」

数日後、Cloudflareのダッシュボードを見て違和感。

リクエスト合計: 1.19k(+5.01%↑)
Cloudflare緩和: 382(-45.82%↓)

リクエストは増えてるのに、緩和数が半減してる。

おかしい。DNS浸透が進めば緩和率は上がるはずでは?

ログを見たら衝撃の事実

サーバーのアクセスログを分析してみました。

Cloudflare経由: 25%
直接アクセス:   75%  ← !?

75%がCloudflareを通っていない。

つまり、攻撃者がオリジンIP(サーバーの実IP)を直接叩いてきている。

攻撃者も学習する

冷静に考えれば当然でした。

Day 1: Cloudflare経由でアクセス

       ブロックされた

Day 2: 「直接IP叩けばいけるじゃん」

Day 3: 直接アクセス75%に

攻撃者だってバカじゃない。防御されたら迂回する。

直接アクセスの中身

687件/日の不審リクエストが直接来ていました。

攻撃パターン 件数/日
PHP webshell探索 388件
.env ファイル探索 143件
.git/config 探索 109件
xmlrpc.php 攻撃 47件

Cloudflareを入れた意味が薄れている状態。

なぜオリジンIPがバレるのか

いくつか原因があります。

  1. DNS履歴: 過去のAレコードが残っている
  2. メールヘッダー: サーバーから送信したメールに記載
  3. サブドメイン: Cloudflareを通していないサブドメイン
  4. Shodan等: IPスキャンサービスに記録されている

一度バレたIPは、攻撃者のリストに残り続けます。

解決策: オリジンを隠す

Cloudflare側でいくら設定しても、直接アクセスは防げません。

サーバー側でCloudflare以外をブロックする必要があります。

iptablesで制限

# Cloudflare IPv4 のみ HTTP/HTTPS を許可
sudo iptables -A INPUT -p tcp --dport 80 -s 173.245.48.0/20 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 80 -s 103.21.244.0/22 -j ACCEPT
# ... 他のCloudflare IPレンジ
sudo iptables -A INPUT -p tcp --dport 80 -j DROP

# HTTPSも同様に設定
sudo iptables -A INPUT -p tcp --dport 443 -s 173.245.48.0/20 -j ACCEPT
# ...
sudo iptables -A INPUT -p tcp --dport 443 -j DROP

Cloudflareの公式IPリストは以下で確認できます。
https://www.cloudflare.com/ips/

永続化を忘れずに

sudo apt-get install -y iptables-persistent
sudo netfilter-persistent save

再起動で設定が消えると悲しいことになります。

設定後の確認

※以下、ドメイン名・IPは仮のものに置き換えています。

# 直接アクセス → タイムアウト = 成功
$ curl -I --max-time 5 http://[サーバーIP]
curl: (28) Connection timed out after 5001 milliseconds

# Cloudflare経由 → 正常
$ curl -I https://example.com
HTTP/2 200

直接アクセスが完全にブロックされました。

Before / After

項目 Before After
直接アクセス 75% 0%
Cloudflare経由 25% 100%
不審リクエスト到達 687件/日 0件
攻撃者の選択肢 直接 or CF経由 CF経由のみ

全トラフィックがCloudflareを通るようになりました。

学んだこと

1. Cloudflareは「玄関」、iptablesは「裏口の鍵」

Cloudflareを入れただけでは、裏口(直接IP)が開いたまま。両方の対策が必要。

2. 攻撃者は適応する

防御を入れると、攻撃者は別の経路を探す。一度の対策で終わりではない。

3. ログを見続けることが大事

「設定したから安心」ではなく、定期的にログを確認する習慣が重要。

今後の経過観察

直接アクセスを塞いだことで、攻撃者の行動がどう変わるか。

予測されるパターン:

  • 諦めて別ターゲットへ → 攻撃減少
  • Cloudflare経由で継続 → WAFでブロック
  • より高度な手法 → 中小サイトにそこまでやる価値は低い

1週間後にまたレポートします。

まとめ

Cloudflare導入

「これで安心」と思った

緩和率が下がってきた

ログ見たら75%バイパス

攻撃者が直接IP叩いてた

iptablesで塞いだ

完全にCloudflare経由に

Cloudflare + iptables の二重防御で、ようやく本当の防御が完成します。

無料プランでもここまでできる。中小サイトのセキュリティ対策の参考になれば。


関連記事


この分析は CloudLogAI で自動検知しました。

👉 仕組みの詳細はこちら:月170円で24時間AI監視

月170円で動くAI監視システムで、攻撃もパフォーマンス低下も早期に検知できます。

「うちのサイト、今どうなってるんだろう?」と思ったら、お気軽にどうぞ。

Discussion

shumaikun0716shumaikun0716

直接IP叩かれるのは怖いですよね...。対策としてはファイアウォールで特定のIPアドレスからのみの通信を許可するようにするか, Cloudflared Tunnelでトンネルを張る方法がありますね。Cloudflared TunnelとWordPressの組み合わせはやったことなくて分からないですが。
ファイアウォールでの対策の方法に使うのはiptablesもいいですけど, nftablesの方がより柔軟に書けると思いますよ。iptablesだと受け入れるIPアドレスが増えたらまたたくさん書かないと行けなくなるので。nftablesなら受け入れるIPアドレスをまとめて指定できるので便利だったりします。今のLinuxのファイアウォールはnftablesがiptables/ip6tables・ebtables・arptables・ufw・firewalldのバックエンドとして動いているのでnftablesの方を触る方が一元管理ができますね。nftablesを使うやり方が賢いかは分からないですが。

/etc/nftables.conf
#!/usr/sbin/nft -f

flush ruleset

table inet filter {
    #elementsの中にIPアドレスの指定をすればOK。CIDR表記もできる。
    #定期的にBGP使ってCloudflareの所有IPを調べて適用するみたいなやり方も。
    set cloudflare_v4 {
        type ipv4_addr
        flags interval
        elements = {
            173.245.48.0/20, 103.21.244.0/22, 103.22.200.0/22,
            103.31.4.0/22, 141.101.64.0/18, 108.162.192.0/18,
            190.93.240.0/20, 188.114.96.0/20, 197.234.240.0/22,
            198.41.128.0/17, 162.158.0.0/15, 104.16.0.0/13,
            104.24.0.0/14, 172.64.0.0/13, 131.0.72.0/22
        }
    }
    set cloudflare_v6 {
        type ipv6_addr
        flags interval
        elements = {
            2400:cb00::/32, 2606:4700::/32, 2803:f800::/32,
            2405:b500::/32, 2405:8100::/32, 2a06:98c0::/29,
            2c0f:f248::/32
        }
    }

    chain input {
        type filter hook input priority 0;

                policy drop;

                ct state { established, related } accept

                iifname "lo" accept
                ip6 nexthdr icmpv6 accept

                icmp type { echo-request, echo-reply } accept
                icmpv6 type { echo-request, echo-reply, nd-neighbor-solicit, nd-neighbor-advert, nd-router-advert } accept

    #dportに指定するポートはサービスに合わせて指定。ip daddrかip6 daddrで宛先も指定可能。
    ip saddr @cloudflare_v4 tcp dport { 80, 443, 8080, 8443 } accept
    ip6 saddr @cloudflare_v6 tcp dport { 80, 443, 8080, 8443 } accept
    #宛先も指定する場合
    ip saddr @cloudflare_v4 ip daddr 192.168.1.254 tcp dport { 80, 443 } accept
    ip6 saddr @cloudflare_v4 ip6 daddr 240b:xxxx:xxxx:xxxx::1 { 80, 443 } accept
    }

    chain forward {
        type filter hook forward priority 0; 
        policy drop;
    }

    chain output {
        type filter hook output priority 0; 
        policy accept;
    }
}
DHDH

コメントありがとうございます!

nftablesの情報、とても参考になります。
iptablesでIPv4/IPv6を別々に書いてて「これ増えたら辛いな」と思ってたところでした。
nftablesならセットでまとめて書けるのは確かに便利ですね。

実は記事の続きでIPv6対応の話も書く予定なので、
nftablesでの書き方も紹介できればと思います。

Cloudflared Tunnelも気になってたので、そちらも調べてみます!

shumaikun0716shumaikun0716

IPアドレス増えたらiptables/ip6tablesで書くの大変ですもんね。過去に私もiptables/ip6tablesでやっていたんですけど, GeoIPLiteで抽出した日本のIPアドレスをrules.v4とrules.v6に書き出すようにして書いていたんですが,しんどすぎてやってられなかったです。iptables/ip6tablesがいくらC/C++でできているとしてもリロードが結構時間かかるんですよね。そこでnftablesに移行したらめっちゃ動作早くてビックリしました。もう戻れないですね。
nftablesはRHEL8以降,Debian 10以降,Ubuntu 20.10以降で標準採用されるようになったらしいです。それ以前のバージョンのものはiptablesですけど結局iptables-nftのようなnftables互換のパッケージなので実質nftablesとそこまで変わらなかったりするかもしれないですね。一応 update-alternativesコマンドでiptablesなどのバージョンを切り替えることもできますね(あんまりするメリットなさそうです...)。
私もnftablesに移行してから浅くてまだまだ勉強中ですが,nftablesの情報が参考になったのであれば良かったです。

DHDH

追加情報ありがとうございます!

GeoIPLiteで日本IP全部書き出すのは確かにしんどそうですね...。
リロード時間の違いも体感できるレベルなんですね。実体験として参考になります。

標準採用のバージョン情報も助かります:

  • RHEL 8以降
  • Debian 10以降
  • Ubuntu 20.10以降

レンタルサーバーだとOSバージョンがバラバラなので、
どっちで書くか判断する材料になりますね。

記事にも追記させていただくかもしれません!