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

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

- 公開日: 2026-06-28
- 著者: 友田 陽大
- タグ: セキュリティ, ネットワーク, TCP/IP, 脆弱性診断, ホワイトハッカー, 可観測性
- URL: https://tomodahinata.com/blog/network-reconnaissance-port-scanning-nmap-service-detection-defense-guide
- カテゴリ: 実践ネットワーク攻撃と防御
- 総合ガイド: https://tomodahinata.com/blog/network-penetration-testing-methodology-attack-defense-guide

## 要点

- ポートスキャンは全攻撃の起点。『どのポートが開き、何のサービスが動いているか』を暴く偵察で、NIST SP 800-115 の発見フェーズの中核。攻撃面が見えなければ攻撃も防御もできない
- TCP SYN スキャン(-sS)は3ウェイハンドシェイクを完了せず SYN→SYN/ACK で『開』を判定し RST で中断する。完全な接続を張らないため『ステルス』と呼ばれるが、現代のIDS/ファイアウォールには筒抜け
- ポート状態は open/closed/filtered の3つ。filtered（応答なし）はファイアウォールの存在を示す。nmap は -sV でサービス版数を、-O で OS を推定し、-sC で既知の弱点を軽く突く
- 防御の第一は『攻撃面の最小化』——開けるポートを必要最小限にし、管理ポートは公開しない。AWS なら Security Group を最小許可で書き、踏み台/SSM 経由にする。これは設定で機械的に達成できる
- 第二は『検知』。スキャンは短時間に多数のポートへ接続を試みる特徴があり、IDS(Suricata)やフローログで検知できる。攻撃面の縮小（予防）と検知（発見）の二段で守る

---

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

この記事は、[ネットワークペンテストの全体像](/blog/network-penetration-testing-methodology-attack-defense-guide)に続き、ポートスキャンの仕組みを [nmap 公式ドキュメント](https://nmap.org/book/man.html)に忠実に解説し、その上で**攻撃面の最小化と検知**という防御を型安全なコードで示します。

> **安全地帯の徹底**：本記事の `nmap` コマンドは、すべて[自宅ラボ](/blog/ethical-hacking-home-lab-kali-juice-shop-ctf-self-study-roadmap-guide)の**自分が管理するVM（例 `10.10.10.10`）**に対してのみ実行します。**無許可の第三者ホスト・公開サーバーへのスキャンは、不正アクセス禁止法の問題に加え、相手のIDSに記録され通報されうる**実害ある行為です。スキャンしてよいのは「自分の資産・CTF・書面で許可されたスコープ」だけ——[法律の記事](/blog/ethical-hacker-law-japan-unauthorized-access-act-active-cyber-defense-disclosure-guide)を必ず先に。

---

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

ポートスキャンは、[TCPの3ウェイハンドシェイク](/blog/tcp-three-way-handshake-state-transition-retransmission-congestion-control-guide)の挙動を逆手に取ります。ポートに SYN を送ったとき、相手の反応で状態が分かるのです。

```text
■ ポートが「開いている（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で隠す余地 |
| **filtered** | FW/SGが遮断 | **理想の状態**。攻撃者に情報を与えない |

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

---

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

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

```bash
# ① ホスト発見：ラボのセグメントで「生きているホスト」を洗い出す（自分の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スクリプト（バナー取得等）
```

**防御側にとっての最大の学び**は ③ です。`-sV` が `nginx 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](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager.html)（ポートを一切開けずに接続）を使う。
- **データベース・内部APIはプライベートサブネット**へ。インターネットから到達不能にする。
- **公開は 443（HTTPS）に集約**し、その背後で経路を分岐させる。

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

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

```hcl
# ❌ 事故の典型：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）が定石

本番では [Suricata](https://suricata.io/) や [Snort](https://www.snort.org/) のようなIDS/IPSが、ポートスキャンを既製ルールで検知します。AWS なら VPC フローログ＋GuardDuty が `Recon:*` の finding を上げます。**まずはマネージド/既製の検知に乗る**のが正解です。

### 4.2 仕組みを理解するための最小実装（型安全）

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

```ts
/** 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）は「見られたら気づく」発見。どちらか一方では不十分です。これは[多層防御](/blog/network-penetration-testing-methodology-attack-defense-guide)の基本姿勢です。

---

## 5. まとめ

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

次は、L2 で経路そのものに割り込む**[ARPスプーフィング / 中間者攻撃（MITM）](/blog/arp-spoofing-mitm-attack-detection-defense-guide)**を扱います。

---

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