Skip to content

DNSの仕組みと設計

DNS(Domain Name System)は、ドメイン名IPアドレスを相互に変換する分散データベースシステムです。この記事では、DNSの動作原理から設計・セキュリティまでを整理します。

graph TB
    subgraph dns_role["DNSの役割"]
        USER["ユーザー"]
        BROWSER["www.example.com<br/>にアクセスしたい"]
        DNS["DNSサーバー"]
        IP["93.184.216.34"]
        WEB["Webサーバー"]
    end

    USER --> BROWSER
    BROWSER -->|"名前解決要求"| DNS
    DNS -->|"IPアドレス応答"| BROWSER
    BROWSER -->|"HTTP通信"| WEB

    style USER fill:#e3f2fd
    style DNS fill:#c8e6c9
    style WEB fill:#fff3e0

graph TB
    ROOT["ルートDNS<br/>(.)"]

    subgraph tld["TLD(トップレベルドメイン)"]
        COM[".com"]
        JP[".jp"]
        ORG[".org"]
    end

    subgraph sld["SLD(セカンドレベル)"]
        EXAMPLE["example.com"]
        COJP["co.jp"]
    end

    subgraph host["ホスト名"]
        WWW["www.example.com"]
        MAIL["mail.example.com"]
    end

    ROOT --> COM
    ROOT --> JP
    ROOT --> ORG
    COM --> EXAMPLE
    JP --> COJP
    EXAMPLE --> WWW
    EXAMPLE --> MAIL

    style ROOT fill:#ffcdd2
    style COM fill:#fff3e0
    style JP fill:#fff3e0
    style ORG fill:#fff3e0
    style EXAMPLE fill:#c8e6c9
    style WWW fill:#e3f2fd
    style MAIL fill:#e3f2fd
レベル説明
ルート最上位、「.」で表記.
TLDトップレベルドメイン.com, .jp, .org
SLDセカンドレベルドメインexample.com
ホスト名個別のホストwww.example.com

種類役割特徴
プライマリDNSゾーン情報の原本を保持ゾーンファイルを直接管理
セカンダリDNSプライマリのコピーを保持ゾーン転送で同期
フルリゾルバ再帰問い合わせを処理キャッシュ機能あり
フォワーダ他のDNSへ転送自身では解決しない

sequenceDiagram
    participant Client as クライアント
    participant Cache as キャッシュDNS
    participant Root as ルートDNS
    participant TLD as TLD DNS
    participant Auth as 権威DNS

    Note over Client,Auth: 再帰クエリ(クライアント→キャッシュDNS)
    Client->>Cache: www.example.comは?

    Note over Cache,Auth: 反復クエリ(キャッシュDNS→各サーバー)
    Cache->>Root: www.example.comは?
    Root-->>Cache: .comはこのサーバー

    Cache->>TLD: www.example.comは?
    TLD-->>Cache: example.comはこのサーバー

    Cache->>Auth: www.example.comは?
    Auth-->>Cache: 93.184.216.34

    Note over Client,Auth: 結果をキャッシュして応答
    Cache-->>Client: 93.184.216.34
  • 再帰クエリ
    • クライアントが送信
    • 完全な回答を要求
    • キャッシュDNSが代わりに解決
  • 反復クエリ
    • キャッシュDNSが送信
    • 参照先を返してもよい
    • 次の問い合わせ先を段階的に辿る

レコード用途
Aレコードホスト名→IPv4www IN A 192.168.1.1
AAAAレコードホスト名→IPv6www IN AAAA 2001:db8::1
CNAME別名の定義blog IN CNAME www
MXレコードメールサーバー指定@ IN MX 10 mail.example.com
NSレコードネームサーバー指定@ IN NS ns1.example.com
TXTレコードテキスト情報@ IN TXT “v=spf1 …”
PTRレコード逆引き(IP→名前)1 IN PTR www.example.com
SOAレコードゾーンの管理情報シリアル番号、更新間隔など
SRVレコードサービスの場所_ldap._tcp IN SRV …

ポイント: 優先度の数値が小さいほど優先される


  • AXFR(フル転送): ゾーン全体を転送。初回同期に使用。データ量が大きい。
  • IXFR(差分転送): 変更分のみ転送。定期同期に使用。効率的。
sequenceDiagram
    participant P as プライマリ
    participant S as セカンダリ

    Note over P,S: 方法1: NOTIFYによる通知
    P->>S: NOTIFY(変更通知)
    S->>P: SOAクエリ
    P-->>S: SOA応答
    S->>P: IXFR要求
    P-->>S: 差分データ

    Note over P,S: 方法2: リフレッシュ間隔
    Note over S: リフレッシュ時間経過
    S->>P: SOAクエリ
    P-->>S: SOA応答
    Note over S: シリアル番号を比較
    S->>P: IXFR要求
    P-->>S: 差分データ
フィールド説明典型値
SERIAL変更を示す番号(増加させる)YYYYMMDDnn
REFRESHセカンダリの確認間隔3600(1時間)
RETRY確認失敗時の再試行間隔600(10分)
EXPIREプライマリ不達時の有効期限604800(1週間)
MINIMUMネガティブキャッシュのTTL3600(1時間)

graph TB
    subgraph ttl["TTL(Time To Live)の動作"]
        QUERY["クエリ"]
        CACHE["キャッシュDNS"]
        STORED["キャッシュに保存<br/>TTL: 3600秒"]
        EXPIRED["TTL期限切れ"]
        REQUERY["再問い合わせ"]
    end

    QUERY --> CACHE
    CACHE -->|"キャッシュなし"| AUTH["権威DNS"]
    AUTH --> STORED
    STORED -->|"3600秒経過"| EXPIRED
    EXPIRED --> REQUERY
    REQUERY --> AUTH

    style CACHE fill:#c8e6c9
    style STORED fill:#e3f2fd
    style EXPIRED fill:#ffcdd2

TTL設計のポイント:

  • 長いTTL: キャッシュ効果大、変更反映が遅い
  • 短いTTL: 変更反映が速い、問い合わせ負荷増大
  • 切り替え前はTTLを短くしておく

graph TB
    subgraph poisoning["キャッシュポイズニング攻撃"]
        CLIENT["クライアント"]
        CACHE["キャッシュDNS"]
        AUTH["正規の権威DNS"]
        ATTACKER["攻撃者"]
    end

    CLIENT -->|"1. クエリ"| CACHE
    CACHE -->|"2. 問い合わせ"| AUTH
    ATTACKER -->|"3. 偽の応答<br/>(先に到達)"| CACHE
    AUTH -.->|"4. 正規応答<br/>(後から到達)"| CACHE
    CACHE -->|"5. 偽のIPを返答"| CLIENT

    style CLIENT fill:#e3f2fd
    style CACHE fill:#ffcdd2
    style AUTH fill:#c8e6c9
    style ATTACKER fill:#ff8a80
  • ソースポートのランダム化: 予測困難にする
  • トランザクションIDのランダム化: 予測困難にする
  • DNSSEC: 電子署名による応答の検証
  • レートリミット: 攻撃の抑制
graph TB
    subgraph dnssec["DNSSECの仕組み"]
        subgraph keys["鍵の種類"]
            ZSK["ZSK<br/>ゾーン署名鍵"]
            KSK["KSK<br/>鍵署名鍵"]
        end

        subgraph records["関連レコード"]
            DNSKEY["DNSKEYレコード<br/>公開鍵"]
            RRSIG["RRSIGレコード<br/>署名"]
            DS["DSレコード<br/>親への委任"]
            NSEC["NSEC/NSEC3<br/>不存在証明"]
        end
    end

    ZSK -->|"署名"| RRSIG
    KSK -->|"署名"| DNSKEY
    KSK -->|"ハッシュ"| DS

    style ZSK fill:#c8e6c9
    style KSK fill:#ffcdd2
    style RRSIG fill:#e3f2fd
    style DS fill:#fff3e0
レコード役割
DNSKEY公開鍵を格納
RRSIGレコードの電子署名
DS親ゾーンへの委任情報(KSKのハッシュ)
NSEC/NSEC3レコードが存在しないことの証明

graph TB
    subgraph redundancy["DNS冗長構成"]
        subgraph primary_site["プライマリサイト"]
            PRIMARY["プライマリDNS"]
        end

        subgraph secondary_site["セカンダリサイト"]
            SECONDARY1["セカンダリDNS 1"]
            SECONDARY2["セカンダリDNS 2"]
        end

        subgraph client_side["クライアント側"]
            RESOLVER["リゾルバ"]
        end
    end

    PRIMARY -->|"ゾーン転送"| SECONDARY1
    PRIMARY -->|"ゾーン転送"| SECONDARY2
    RESOLVER -->|"問い合わせ"| PRIMARY
    RESOLVER -.->|"フェイルオーバー"| SECONDARY1

    style PRIMARY fill:#ffcdd2
    style SECONDARY1 fill:#c8e6c9
    style SECONDARY2 fill:#c8e6c9
graph TB
    subgraph split["スプリットDNS構成"]
        subgraph external["外部ゾーン"]
            EXT_DNS["外部DNS"]
            EXT_REC["公開サーバーのみ<br/>www, mail"]
        end

        subgraph internal["内部ゾーン"]
            INT_DNS["内部DNS"]
            INT_REC["全サーバー<br/>www, mail, app, db"]
        end

        INTERNET["インターネット"]
        INTRANET["社内ネットワーク"]
    end

    INTERNET -->|"問い合わせ"| EXT_DNS
    INTRANET -->|"問い合わせ"| INT_DNS

    style EXT_DNS fill:#fff3e0
    style INT_DNS fill:#c8e6c9
    style EXT_REC fill:#fff3e0
    style INT_REC fill:#c8e6c9

スプリットDNSのメリット:

  • 内部サーバー情報を外部に公開しない
  • 内部では詳細な名前解決が可能
  • セキュリティの向上

; SOAレコード
example.com. IN SOA ns1.example.com. admin.example.com. (
2024011801 ; シリアル番号
3600 ; リフレッシュ(1時間)
600 ; リトライ(10分)
604800 ; 有効期限(1週間)
3600 ; ネガティブキャッシュTTL
)
; NSレコード
example.com. IN NS ns1.example.com.
example.com. IN NS ns2.example.com.
; Aレコード
ns1 IN A 192.168.1.10
ns2 IN A 192.168.1.11
www IN A 192.168.1.100
mail IN A 192.168.1.50
; MXレコード
@ IN MX 10 mail.example.com.
@ IN MX 20 mail2.example.com.
; CNAMEレコード
blog IN CNAME www
; TXTレコード(SPF)
@ IN TXT "v=spf1 mx ip4:192.168.1.0/24 -all"

  1. クエリの種類を理解する

    • 再帰クエリ: クライアント→キャッシュDNS
    • 反復クエリ: キャッシュDNS→各権威DNS
  2. レコードタイプを正確に覚える

    • A: IPv4、AAAA: IPv6
    • MX: メール(優先度は小さいほど優先)
    • CNAME: 他のAレコードのエイリアス
  3. ゾーン転送の仕組み

    • AXFR: 全体転送、IXFR: 差分転送
    • NOTIFYによるプッシュ通知
  4. セキュリティ対策

    • DNSSECの仕組み(ZSK, KSK, RRSIG, DS)
    • キャッシュポイズニングの原理と対策
  5. 設計パターン

    • プライマリ/セカンダリの冗長構成
    • スプリットDNSによる内外分離