Cloudflare WAF を普通に入れても迂回されます →オリジン直アクセスを塞ぐ設定方法

最近のAIエージェント攻撃による サイバーセキュリティを気にしてCloudflareのWAFを入れるサイト管理者が多いと思いますが、 通常に入れただけでは100%攻撃を防いでいるわけではありません。 なぜならWAFを回避するアクセス方法があるからです
1. Cloudflare WAFの設定方法
- Cloudflare にサイト(ドメイン)を追加する
- 自動で取り込まれた既存の DNS レコードに、漏れや不要なものがないか確認する
- ドメインを登録しているレジストラで、ネームサーバーを Cloudflare が指定するものに変更する
- Cloudflare の管理画面で、ドメインが「有効(Active)」になったことを確認する
- Web 用の A / AAAA / CNAME レコードはプロキシ(オレンジ雲)を有効にする(WAF はプロキシが有効なレコードにしか効かない)
- MX などメール用のレコードはプロキシできないため、Web サーバーと同じ IP を指さないようにする
- SSL/TLS モードを「Full (strict)」にする
- マネージドルール(Cloudflare Managed Ruleset、OWASP)を有効にする
- カスタムルール(国、パス、User-Agent などでの制限)
- レート制限ルール
- Bot Fight Mode / Bot 対策
2. 落とし穴:WAF はオリジンへの直接アクセスを守れない
Cloudflare の WAF が効くのは、Cloudflare を経由した通信だけです。 オリジンサーバーの IP アドレスへ直接アクセスされると、WAF を通らずにサイトへ届いてしまいます。
実際にあるサイトで検証した結果がこちらです。
| アクセス方法 | 結果 |
|---|---|
| IP アドレスだけで接続(HTTP / HTTPS) | サーバー構成によるが200が返ってくる可能性もある❌ |
| ドメイン名を指定して IP へ直接接続 | 200(サイトが表示される)❌ |
攻撃者は次のコマンドで同じことが簡単にできます。
curl -ks --resolve 【ドメイン名】:443:【IPアドレス】 https://【ドメイン名】/「IP 直打ちで 404 になるから大丈夫」ではない、という点に注意してください。
3. 対策:オリジンへの直接アクセスを塞ぐ方法
- ① ファイアウォールで Cloudflare の IP レンジだけを許可する(iptables/ufw、AWS のセキュリティグループなど)
- ② Authenticated Origin Pulls(mTLS で、Cloudflare から来た通信かどうかを検証する)
- ③ Cloudflare Tunnel(cloudflared)を使い、受信用のポートを開けない
- ④ (補助策)秘密のヘッダーを付けて検証する方法と、その弱点
- 各方法の比較表(手間、強さ、向いている環境)
4. オリジン IP はどうやって漏れるか
- 最初から Cloudflare なら大丈夫? → NO
- 先に入れることで防げるもの:導入前の A レコードが DNS 履歴サービスに残ること
- 先に入れても防げないもの:以下の経路
- インターネット全体のスキャン(Censys、Shodan)
- ホスト名(SNI)なしで IP に接続したときに返る証明書から、IP とドメインが結び付けられる
- ページのタイトル、本文、favicon のハッシュからも照合される
- Cloudflare を通していないレコード(MX、
mail.、ftp.、dev.などのサブドメイン) - サーバーから外へ出る通信(メールの Received ヘッダー、Webhook、外部 URL の取得機能や SSRF、pingback)
- 同じ IP で Cloudflare なしで公開している別のサイト
- アプリ側からの漏れ(エラーページ、phpinfo、デバッグ出力)
- 結論:「IP は漏れるもの」と考えて、漏れても通さない設計にする
5. 設定後に気をつけるポイント
- Cloudflare の IP レンジは変わることがあるので、定期的に更新する
- IPv6 と、80/443 以外のポートも忘れない
- 本当のアクセス元 IP を取り出す設定(
CF-Connecting-IP、mod_remoteip / real_ip) - SSH などの管理用アクセスの経路は別に確保する
- Cloudflare をオフ(灰色雲)にしたときの影響
6. WAFが迂回アクセスに対しても有効かどうかの確認方法
- オリジンへの直接アクセスを試すコマンド:
curl -ks --resolve 【ドメイン名】:443:【IPアドレス】 https://【ドメイン名】/ | head -c 2000- 拒否またはタイムアウトになれば対策が効いています。
- HTML が返ってくれば迂回できてしまう状態❌です。
- IP だけでの HTTP/HTTPS 接続、IPv6 での接続も確認しましょう
7. シェルスクリプトによる一括確認
こちらのシェルを実行してみてください。結果が表示されます
シェルスクリプト
以下の内容を check-origin-block.sh という名前で保存します。
#!/usr/bin/env bash
#
# check-origin-block.sh
# Cloudflare を迂回したオリジンへの直接アクセスがブロックできているかを判定する
#
# 使い方:
# ./check-origin-block.sh <ドメイン名> <IPv4アドレス> [IPv6アドレス]
#
# 例:
# ./check-origin-block.sh example.com 203.0.113.10
# ./check-origin-block.sh example.com 203.0.113.10 2001:db8::1
#
# 判定:
# OK : 接続できない(拒否・タイムアウト・TLSエラーなど)、4xx/5xx でサイトが表示されない、
# または同じドメインの https:// へ転送される
# NG : 2xx や、上記以外への 3xx が返る(WAF を迂回できる)
#
# 終了コード: すべて OK なら 0、NG が1つでもあれば 1、引数エラーは 2
set -u
CONNECT_TIMEOUT=5
MAX_TIME=10
SNIPPET_BYTES=300
if [ -t 1 ]; then
C_OK=$'\033[32m'; C_NG=$'\033[31m'; C_DIM=$'\033[2m'; C_RESET=$'\033[0m'
else
C_OK=''; C_NG=''; C_DIM=''; C_RESET=''
fi
usage() {
echo "使い方: $0 <ドメイン名> <IPv4アドレス> [IPv6アドレス]" >&2
exit 2
}
[ $# -lt 2 ] || [ $# -gt 3 ] && usage
DOMAIN="$1"
IPV4="$2"
IPV6="${3:-}"
TMP_BODY="$(mktemp)"
trap 'rm -f "$TMP_BODY"' EXIT
NG_COUNT=0
TOTAL=0
# check <説明> <curl に渡す追加引数...> -- <URL>
check() {
local label="$1"; shift
local args=()
while [ "$1" != "--" ]; do args+=("$1"); shift; done
shift
local url="$1"
TOTAL=$((TOTAL + 1))
: > "$TMP_BODY"
local code
code="$(curl -gks --noproxy '*' \
--connect-timeout "$CONNECT_TIMEOUT" --max-time "$MAX_TIME" \
-o "$TMP_BODY" -w '%{http_code} %{redirect_url}' \
"${args[@]+"${args[@]}"}" "$url" 2>/dev/null)"
local rc=$?
local redirect="${code#* }"
code="${code%% *}"
if [ "$rc" -ne 0 ] || [ "$code" = "000" ]; then
printf '%s[OK]%s %s\n' "$C_OK" "$C_RESET" "$label"
printf ' %s接続できませんでした(curl 終了コード %s)。ブロックできています。%s\n' "$C_DIM" "$rc" "$C_RESET"
elif [ "$code" -ge 400 ]; then
printf '%s[OK]%s %s\n' "$C_OK" "$C_RESET" "$label"
printf ' %sHTTP %s が返りました。サーバーには届いていますが、サイトは表示されません。%s\n' "$C_DIM" "$code" "$C_RESET"
elif [ "$code" -ge 300 ] && [[ "$redirect" == "https://${DOMAIN}/"* ]]; then
printf '%s[OK]%s %s\n' "$C_OK" "$C_RESET" "$label"
printf ' %sHTTP %s で %s へ転送されます。サイトは表示されません。%s\n' "$C_DIM" "$code" "$redirect" "$C_RESET"
else
NG_COUNT=$((NG_COUNT + 1))
printf '%s[NG]%s %s\n' "$C_NG" "$C_RESET" "$label"
printf ' HTTP %s が返りました。Cloudflare(WAF)を迂回してアクセスできてしまいます。\n' "$code"
printf ' %s--- 応答の先頭 %s バイト ---%s\n' "$C_DIM" "$SNIPPET_BYTES" "$C_RESET"
head -c "$SNIPPET_BYTES" "$TMP_BODY" | sed 's/^/ /'
echo
fi
}
echo "対象ドメイン: $DOMAIN"
echo "IPv4 : $IPV4"
[ -n "$IPV6" ] && echo "IPv6 : $IPV6"
echo
echo "== IPv4 =="
check "ドメイン名を指定して IPv4 へ直接接続(HTTPS)" \
--resolve "${DOMAIN}:443:${IPV4}" -- "https://${DOMAIN}/"
check "ドメイン名を指定して IPv4 へ直接接続(HTTP)" \
--resolve "${DOMAIN}:80:${IPV4}" -- "http://${DOMAIN}/"
check "IPv4 アドレスだけで接続(HTTPS)" -- "https://${IPV4}/"
check "IPv4 アドレスだけで接続(HTTP)" -- "http://${IPV4}/"
if [ -n "$IPV6" ]; then
echo
echo "== IPv6 =="
if ! curl -6 -s --connect-timeout "$CONNECT_TIMEOUT" -o /dev/null https://api64.ipify.org 2>/dev/null; then
echo "注意: この環境から IPv6 で外部に接続できないようです。"
echo " 以下の IPv6 の結果が [OK] でも、ブロックされているとは限りません。"
echo " IPv6 が使える別の環境から確認してください。"
fi
check "ドメイン名を指定して IPv6 へ直接接続(HTTPS)" \
--resolve "${DOMAIN}:443:[${IPV6}]" -- "https://${DOMAIN}/"
check "ドメイン名を指定して IPv6 へ直接接続(HTTP)" \
--resolve "${DOMAIN}:80:[${IPV6}]" -- "http://${DOMAIN}/"
check "IPv6 アドレスだけで接続(HTTPS)" -- "https://[${IPV6}]/"
check "IPv6 アドレスだけで接続(HTTP)" -- "http://[${IPV6}]/"
fi
echo
if [ "$NG_COUNT" -eq 0 ]; then
printf '%s結果: OK%s(%s 件すべてブロックできています)\n' "$C_OK" "$C_RESET" "$TOTAL"
exit 0
else
printf '%s結果: NG%s(%s 件中 %s 件で直接アクセスできてしまいます)\n' "$C_NG" "$C_RESET" "$TOTAL" "$NG_COUNT"
exit 1
fi実行方法
保存したファイルに実行権限を付けて、ドメイン名とオリジンサーバーの IP アドレスを指定して実行します。
chmod +x check-origin-block.sh
# IPv4 だけ確認する場合
./check-origin-block.sh 【ドメイン名】 【IPv4アドレス】
# IPv6 も確認する場合
./check-origin-block.sh 【ドメイン名】 【IPv4アドレス】 【IPv6アドレス】結果例:

オリジンの IP アドレスは、Cloudflare のプロキシを有効にしていると dig では調べられません(Cloudflare のアドレスが返るため)。サーバー上で ip addr を実行するか、VPS やクラウドの管理画面で確認してください。
確認する内容
IPv4(IPv6 を指定した場合は IPv6 も)について、次の4つを確認します。
- ドメイン名を指定して IP へ直接接続(HTTPS)
- ドメイン名を指定して IP へ直接接続(HTTP)
- IP アドレスだけで接続(HTTPS)
- IP アドレスだけで接続(HTTP)
結果の見方
- [OK]:接続できない(拒否・タイムアウトなど)、4xx/5xx が返ってサイトが表示されない、または同じドメインの
https://へ転送される - [NG]:2xx や、それ以外への 3xx が返る(WAF を迂回できる状態❌)。証拠として応答の先頭300バイトを表示します
NG がある場合の表示例:
対象ドメイン: example.com
IPv4 : 203.0.113.10
== IPv4 ==
[NG] ドメイン名を指定して IPv4 へ直接接続(HTTPS)
HTTP 200 が返りました。Cloudflare(WAF)を迂回してアクセスできてしまいます。
--- 応答の先頭 300 バイト ---
<!DOCTYPE html><html lang="ja"><head>...
[OK] ドメイン名を指定して IPv4 へ直接接続(HTTP)
接続できませんでした(curl 終了コード 7)。ブロックできています。
[OK] IPv4 アドレスだけで接続(HTTPS)
HTTP 404 が返りました。サーバーには届いていますが、サイトは表示されません。
[OK] IPv4 アドレスだけで接続(HTTP)
HTTP 404 が返りました。サーバーには届いていますが、サイトは表示されません。
結果: NG(4 件中 1 件で直接アクセスできてしまいます)IPv6 を指定したときに、実行している環境で IPv6 が使えない場合は注意が表示されます。その場合、IPv6 の結果が [OK] でも当てにならないので、IPv6 が使える別の環境から確認してください。
8. 当事務所にてお手伝いさせていただいております
ウェブサイトのセキュリティなど不安な方は当事務所にてセキュリティチェックや修正運用方法などお手伝いさせていただいております。
ご相談などございましたら CONTACT から何なりとお問合せくださいませ。