メインコンテンツへスキップ
友田 陽大
実践ネットワーク攻撃と防御
セキュリティ
ネットワーク
TCP/IP
脆弱性診断
ホワイトハッカー
可観測性

ポートスキャン・サービス偵察(nmap)の仕組みと防御【2026】— 攻撃面の可視化とRFC準拠の縮小

ネットワーク偵察の中核『ポートスキャン』を、nmap 公式ドキュメントと NIST SP 800-115 に忠実に解説。ホスト発見・TCP SYN スキャン・バージョン/OS 検出の仕組みと、ステルススキャンがなぜ検知されるかを、TCP の状態遷移から説明。すべて自分のラボに閉じた合法手順で、攻撃面の最小化・IDS 検知・セキュリティグループ設計という防御まで型安全なコードで示します。

公開日
読了時間
9分
著者
友田 陽大
シェア

ネットワーク攻撃は、ほぼ例外なくポートスキャンから始まります。攻撃者がまずやることは「どのホストが生きていて、どのポートが開いていて、そこで何が動いているか」を知ること——NIST SP 800-115発見(Discovery)フェーズの中核です。逆に言えば、防御側が自分の攻撃面を nmap で正確に把握していないなら、攻撃者の方が自社のネットワークに詳しいという危険な状態にあります。

この記事は、ネットワークペンテストの全体像に続き、ポートスキャンの仕組みを nmap 公式ドキュメントに忠実に解説し、その上で攻撃面の最小化と検知という防御を型安全なコードで示します。

安全地帯の徹底:本記事の nmap コマンドは、すべて自宅ラボの**自分が管理するVM(例 10.10.10.10)**に対してのみ実行します。無許可の第三者ホスト・公開サーバーへのスキャンは、不正アクセス禁止法の問題に加え、相手のIDSに記録され通報されうる実害ある行為です。スキャンしてよいのは「自分の資産・CTF・書面で許可されたスコープ」だけ——法律の記事を必ず先に。


1. ポートスキャンの原理 — TCP の握手を「途中でやめる」

ポートスキャンは、TCPの3ウェイハンドシェイクの挙動を逆手に取ります。ポートに SYN を送ったとき、相手の反応で状態が分かるのです。

■ ポートが「開いている(open)」場合
  scanner ── SYN ──► target:port      "開いてる?"
  scanner ◄─ SYN/ACK ─ target         "うん、開いてるよ"   ← これで「open」と判定
  scanner ── RST ──► target           "やっぱやめた"(接続を張らずに中断)

■ ポートが「閉じている(closed)」場合
  scanner ── SYN ──► target:port
  scanner ◄─ RST ──── target          "そのポートは閉じてる"   ← 「closed」と判定

■ ファイアウォールに「遮断されている(filtered)」場合
  scanner ── SYN ──► target:port
  (無応答 / ICMP unreachable)          ← 「filtered」と判定(FWの存在が分かる)

この「SYN を送って SYN/ACK が返れば開、ただし ACK は返さず RST で中断する」のが **TCP SYN スキャン(-sS、ハーフオープンスキャン)**です。完全な接続(ESTABLISHED)を張らないため、かつては「ステルス」と呼ばれました。

1.1 「ステルススキャン」は現代では筒抜け

重要な誤解を正します。SYN スキャンは現代のIDS/ファイアウォールには容易に検知されます。理由は単純で、「短時間に大量のポートへSYNだけを送り、SYN/ACKに対してACKを返さずRSTで中断する」という挙動自体が、正常な通信ではありえない明確なシグネチャだからです。攻撃側に「ステルス」の幻想を持たせないことが、防御側の出発点です(§4でこの検知を実装します)。

1.2 状態の3分類が「防御の地図」になる

状態意味防御側の読み方
openサービスが待ち受け中攻撃面。本当に公開が必要かを問う
closed応答するがサービスなしホストの存在は露見。FWで隠す余地
filteredFW/SGが遮断理想の状態。攻撃者に情報を与えない

防御の目標は明確です——公開すべきでないポートを open から filtered へ移すこと。


2. 偵察の手順 — ラボで「自分の攻撃面」を見る

自分のVMに対して、攻撃者と同じ視点で攻撃面を可視化します。

# ① ホスト発見:ラボのセグメントで「生きているホスト」を洗い出す(自分のVMのみ)
nmap -sn 10.10.10.0/24
#   -sn = ポートスキャンせずホスト発見だけ(ping sweep)

# ② TCP SYN スキャン:開いているポートを特定(自分のVM 10.10.10.10 のみ)
sudo nmap -sS 10.10.10.10
#   sudo が要るのは raw socket で SYN を直接組み立てるため

# ③ サービス・バージョン検出:開いたポートで「何が動いているか」を特定
nmap -sV 10.10.10.10
#   例: 22/tcp open ssh OpenSSH 8.9 / 80/tcp open http nginx 1.24.0
#   ↑ 版数が分かると、その版の既知脆弱性(CVE)に直結する=最も危険な情報漏れ

# ④ OS 推定 + デフォルトスクリプト(軽い既知チェック)
sudo nmap -O -sC 10.10.10.10
#   -O  = TCP/IP スタックの癖から OS を推定
#   -sC = 安全寄りのデフォルトNSEスクリプト(バナー取得等)

防御側にとっての最大の学びは ③ です。-sVnginx 1.24.0 のようにバージョンまで露出させると、攻撃者は「その版の既知CVE」を即座に引けます。サービスバナーの版数を隠す/最新に保つことが、いかに重要かが体感できます。

nmap の出力は -oX scan.xml でXML保存できます。診断レポートや、後述の「自分の攻撃面の継続監視」に使えます。自分の資産の棚卸しとしての nmap は、防御側の正当な必須スキルです。


3. 防御①:攻撃面の最小化 — 設定で機械的に潰す

スキャンへの最強の防御は、そもそも開いているポートを減らすことです。これは設計と設定で機械的に達成できます。

3.1 原則 — 「必要最小限の公開」

  • 管理ポート(SSH:22・RDP:3389・DB:5432/3306)を公開しない。踏み台(bastion)か、AWS なら SSM Session Manager(ポートを一切開けずに接続)を使う。
  • データベース・内部APIはプライベートサブネットへ。インターネットから到達不能にする。
  • 公開は 443(HTTPS)に集約し、その背後で経路を分岐させる。

3.2 AWS セキュリティグループを「最小許可」で書く

クラウドでは、ファイアウォール=セキュリティグループ(SG)の設計がそのまま攻撃面になります。0.0.0.0/0 でSSHを開けるのは典型的な事故です。

# ❌ 事故の典型:SSH を全世界に開放(スキャンで即発見され総当たりの的に)
resource "aws_security_group_rule" "bad_ssh" {
  type        = "ingress"
  from_port   = 22
  to_port     = 22
  protocol    = "tcp"
  cidr_blocks = ["0.0.0.0/0"] # ← 攻撃面を全世界に晒している
  security_group_id = aws_security_group.app.id
}

# ✅ 正:公開は 443 のみ。管理は SSM 経由でポートを開けない
resource "aws_security_group" "app" {
  name        = "app-minimal-surface"
  description = "Public 443 only; admin via SSM (no inbound SSH)"
  vpc_id      = var.vpc_id

  ingress {
    description = "HTTPS from anywhere (terminate at ALB/WAF)"
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
  # SSH(22) の ingress は「無い」のが正解。SSM がポートレスで代替する。

  egress {
    description = "Allow outbound (tighten per workload)"
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
  tags = { Name = "app-minimal-surface" }
}

ポイント:攻撃面の最小化は「気をつける」ではなくコード(IaC)で表現し、レビューと差分で守る。これは私が AWS で多層ネットワークを設計・運用してきた中核の考え方です。


4. 防御②:スキャンを検知する — 型安全な検知ロジック

予防(攻撃面の縮小)をすり抜けても、スキャンには明確な行動シグネチャがあります:「短時間に、1つの送信元が、多数の異なるポート/ホストへ接続を試みる」。これを検知します。

4.1 本番では IDS(Suricata)が定石

本番では SuricataSnort のようなIDS/IPSが、ポートスキャンを既製ルールで検知します。AWS なら VPC フローログ+GuardDuty が Recon:* の finding を上げます。まずはマネージド/既製の検知に乗るのが正解です。

4.2 仕組みを理解するための最小実装(型安全)

検知の原理を理解するため、フローログ(または接続イベント)から「スキャンらしさ」を判定する純粋関数を書きます。本番ロジックではなく原理の可視化です。

/** 1件の接続試行(フローログの1行を正規化したもの)。 */
interface ConnectionAttempt {
  readonly srcIp: string;
  readonly dstPort: number;
  readonly timestamp: number; // epoch ms
}

interface ScanVerdict {
  readonly srcIp: string;
  readonly distinctPorts: number;
  readonly isLikelyScan: boolean;
}

/**
 * 「短時間に同一送信元が多数の異なるポートへ接続を試みた」かを判定する純粋関数。
 * 副作用なし=単体テスト・ゴールデンベクタ固定が容易(テスト容易性)。
 *
 * @param windowMs 観測窓(既定60秒)
 * @param portThreshold この窓で触れた異なるポート数の閾値(既定15)
 */
export function detectPortScan(
  attempts: readonly ConnectionAttempt[],
  now: number,
  windowMs = 60_000,
  portThreshold = 15,
): readonly ScanVerdict[] {
  const since = now - windowMs;
  const portsBySrc = new Map<string, Set<number>>();

  for (const a of attempts) {
    if (a.timestamp < since) continue; // 窓の外は無視
    const ports = portsBySrc.get(a.srcIp) ?? new Set<number>();
    ports.add(a.dstPort);
    portsBySrc.set(a.srcIp, ports);
  }

  return [...portsBySrc.entries()]
    .map(([srcIp, ports]) => ({
      srcIp,
      distinctPorts: ports.size,
      isLikelyScan: ports.size >= portThreshold,
    }))
    .filter((v) => v.isLikelyScan)
    .sort((a, b) => b.distinctPorts - a.distinctPorts); // 重い順に並べる(決定的)
}

この純粋関数なら、フローログのバッチや CloudWatch Logs のサブスクリプションから呼び出して、Slack 通知や自動ブロック(許可リストを壊さない範囲で)につなげられます。検知ロジックを副作用から切り離すことで、テストで挙動を固定でき、誤検知(自社の正当なスキャン・ヘルスチェック)を許可リストで安全に除外できます。

予防と検知は別物・両方要る:攻撃面の最小化(§3)は「そもそも見せない」予防、スキャン検知(§4)は「見られたら気づく」発見。どちらか一方では不十分です。これは多層防御の基本姿勢です。


5. まとめ

  • ポートスキャンは全攻撃の起点。SYN スキャン(-sS)は握手を途中でやめて open/closed/filtered を判定する。「ステルス」は幻想で、現代のIDSには筒抜け。
  • 最も危険な情報漏れはサービスの版数-sV)。既知CVEに直結する。版数を隠し、最新に保つ。
  • 防御①=攻撃面の最小化:管理ポートを公開せず、SGを最小許可でIaC化し、SSM/踏み台で代替。openfiltered に移す。
  • 防御②=検知:スキャンの行動シグネチャ(短時間・多ポート・単一送信元)をIDS/フローログで捉える。検知ロジックは純粋関数で分離しテスト可能に。
  • 自分の nmap で自社を棚卸しするのは、防御側の正当な必須スキル。攻撃者より自社に詳しくあれ。

次は、L2 で経路そのものに割り込む**ARPスプーフィング / 中間者攻撃(MITM)**を扱います。


私(友田 陽大)は、AWS のセキュリティグループ・NACL・VPC 設計を最小権限で IaC 化し、GuardDuty・フローログによる偵察検知まで含めた攻撃面管理を実装してきました。「自社の公開ポートを棚卸ししたい」「SG が 0.0.0.0/0 だらけで不安」「スキャン/偵察を検知したい」——こうした攻撃面の可視化と縮小を、攻撃者の視点で診断し、設定(IaC)と検知の両輪で根治します。お気軽にご相談ください。

友田

友田 陽大

経済産業大臣賞 受賞プロダクト開発者。TypeScript + Python + AWS で、SaaS・業界DX・ 実用レベルの生成AI(RAG)を、要件定義からインフラ・運用まで一人で完遂します。

この攻撃、あなたのネットワークで成立しませんか?

ネットワーク/インフラのペネトレーションテスト・堅牢化を承ります

この記事で扱った ARPスプーフィング・DNS汚染・セッションハイジャック・SYNフラッド・盗聴といった L2〜L4 の攻撃を、御社の構成で『どこが破れるか』を攻撃者の視点で診断し、RFC準拠の防御(DAI・DNSSEC・RFC 5961・BCP 38・TLS/mTLS・WAF/DDoS防御)まで設計・実装します。AWS マルチアカウントで多層ネットワーク(VPC・最小権限IAM・GuardDuty・WAF)を構築してきた知見で、攻撃面の最小化とゼロトラスト化を伴走します。

プロジェクト単位(請負)・技術顧問のどちらにも対応可能です。まずは30分の無料技術相談から。

あわせて読みたい