TCP 3-way handshake とは何ぞや
基本的なことでも、よくわかってないことはいっぱいあって、TCPなんて普段そこまで意識しないので、復習がてらのまとめです。
TCP 3-way Handshake: インターネット通信の基盤
TCP/IPネットワークで確実な通信を確立するために、TCPプロトコルは 3-way handshake(スリーウェイハンドシェイク)と呼ばれる3段階の接続確立メカニズムを採用しています。これはクライアントとサーバー間で初期シーケンス番号を交換し、双方が通信準備状態にあることを確認するプロセスです。
3段階のパケット交換
TCP 3-way handshakeは、以下の3つの明確な段階で構成されます。
第1段階: クライアントからSYN送信
クライアントがサーバーへの接続を開始する際、SYN(Synchronize)フラグを1に設定した初期パケットを送信します。このパケットには以下の情報が含まれます。
| 要素 | 値 | 説明 |
|---|---|---|
| SYNフラグ | 1 | 接続開始要求を示す |
| ACKフラグ | 0 | 初回送信のため確認応答なし |
| Sequence Number (Seq) | x | クライアント側の初期シーケンス番号(ランダム値) |
| Acknowledgment Number (Ack) | 0 | 初回なので意味を持たない |
ISN(Initial Sequence Number)xはセキュリティ上の理由から、暗号学的に安全な乱数生成器で生成される32ビットのランダム値です。古い実装では固定値やシンプルなカウンタが使用されていましたが、攻撃者がシーケンス番号を予測してセッションハイジャックを行うリスクがあるため、現代のOSではすべてランダム化されています。
第2段階: サーバーからSYN+ACK返信
サーバーは受信したSYNパケットに対して、SYNとACK両方のフラグを1に設定した応答パケットを返します。
| 要素 | 値 | 説明 |
|---|---|---|
| SYNフラグ | 1 | サーバー側も接続開始要求を示す |
| ACKフラグ | 1 | クライアントのSYNを受信したことの確認 |
| Sequence Number (Seq) | y | サーバー側の初期シーケンス番号(ランダム値) |
| Acknowledgment Number (Ack) | x+1 | クライアントのシーケンス番号に+1 |
ここで重要なのがACK番号がクライアントのシーケンス番号+1という点です。TCPでは相手が送信したシーケンス番号に+1した値を応答することで、SYNパケット(バイト数で1バイト分)を正しく受信したことを示すメカニズムが採用されています。
第3段階: クライアントからACK返信
最後に、クライアントはサーバーからのSYN+ACKに対して確認応答を送信します。この段階でTCP接続が確立されます。
| 要素 | 値 | 説明 |
|---|---|---|
| SYNフラグ | 0 | 既にSYNは完了しているため |
| ACKフラグ | 1 | サーバーのSYNに対する確認応答 |
| Sequence Number (Seq) | x+1 | クライアントの次のシーケンス番号 |
| Acknowledgment Number (Ack) | y+1 | サーバーのシーケンス番号に+1 |
3段階目のパケットは重要な特性を持ちます。このACK応答に実際のデータペイロードを含めることができるという点です。つまり、HTTPリクエストなどのアプリケーションデータを、3段階目のパケットに含めて送信可能なため、接続確立と同時にデータ転送を開始できます。
全体の流れと状態遷移
クライアント サーバー
| |
| SYN (Seq=x, Ack=0) |
|----------------------------------->|
| [SYN_SENT状態] [SYN_RECEIVED状態]
| |
| SYN+ACK (Seq=y, Ack=x+1) |
|<-----------------------------------|
| |
| ACK (Seq=x+1, Ack=y+1) |
|----------------------------------->|
| [ESTABLISHED状態] [ESTABLISHED状態]
| |
ハンドシェイク完了後は、双方が相手のシーケンス番号(x+1とy+1)を基点にして、データ送受信を開始します。HTTPS接続の場合、TCP接続確立後にさらにTLSハンドシェイクが追加で実行されます。
シーケンス番号とACK番号の詳細
シーケンス番号(Sequence Number)の機能
TCP通信では大規模なデータをMSS(Maximum Segment Size)単位に分割して送信します。各セグメントには「何番目のデータか」を示す**シーケンス番号(32ビット符号なし整数)**が付与されます。
- 通し番号機能: データの正確な順序を再構成できる
- 遅延/再送対応: パケットがネットワーク上で遅延したり重複しても、シーケンス番号により正しく並べ替え可能
- 範囲: 0〜4,294,967,295(約43億)の値。この大きな範囲により、攻撃者がシーケンス番号を予測することが実質的に困難
ACK番号(Acknowledgment Number)の意味
受信側が送信側に対して「データをXXXまで受信したよ!次はXXXから送ってね」と伝える番号です。
実例として、サーバーが2つのパケットで1,973バイトを送信した場合を考えます。
- パケット1: Seq=1209, Len=1208バイト(1209〜2416番地のデータ)
- パケット2: Seq=2417, Len=765バイト(2417〜3181番地のデータ)
クライアントがこれら全てを受け取った場合、ACK応答ではAck=3182と設定します。これは「3181番地までのデータを受信確認した、次は3182番地から送ってください」という意味になります。
パラメータの交渉
MSS(Maximum Segment Size)の協議
ハンドシェイク中に、両端末は自身が受信可能な**最大セグメントサイズ(MSS)**をTCPオプション内で相互通知します。
例えば、クライアントがMSS=1460バイト、サーバーがMSS=500バイトを宣言した場合、通信では低い方の500バイトが使用されます。これにより、基盤となるIP層で必要なフラグメンテーション(パケット分割)を回避できます。
MSS値は一般的にMTU(Maximum Transmission Unit)から以下の計算で決定されます。
MSS = MTU - IP_HEADER - TCP_HEADER
= 1500 - 20 - 20 = 1460 bytes(イーサネットの標準的な場合)
Window Size(受信ウィンドウサイズ)
ウィンドウサイズは各ホストが同時に受信可能な最大データ量を示します。このサイズは交渉されるのではなく、各ホストが独立して相手に通知する値です。
ハンドシェイク時点では、ウィンドウフィールド自体はスケーリングされません。RFC 1323で規定されているように、SYNおよびSYN/ACKパケット内のウィンドウフィールドは最大65,535バイトの固定値となります。実際のウィンドウスケーリングは、ハンドシェイク時にスケーリングファクター(ws)が交渉され、その後のパケットから適用されます。
TCP状態遷移
クライアント側とサーバー側で異なる状態遷移を経ります。
クライアント側:
| 状態 | 説明 |
|---|---|
| CLOSED | 初期状態 |
| SYN_SENT | SYNを送信後、SYN+ACKを待機中 |
| ESTABLISHED | ハンドシェイク完了、データ転送可能 |
サーバー側:
| 状態 | 説明 |
|---|---|
| LISTEN | クライアント接続受け入れ待ち |
| SYN_RECEIVED | クライアントのSYNを受信、ACKを待機中 |
| ESTABLISHED | ハンドシェイク完了、データ転送可能 |
タイミングとRTT(Round Trip Time)
TCP 3-way handshakeは1.5 RTTで完了します。
- 0→0.5 RTT: クライアント→サーバーへSYN送信
- 0.5→1.0 RTT: サーバー→クライアントへSYN+ACK返信(クライアント側は接続準備完了)
- 1.0→1.5 RTT: クライアント→サーバーへACK送信(サーバー側も接続確立、ESTABLISHED状態へ)
3段階目のACKにはデータペイロードを含めることができるため、実質的に1 RTT経過時点でクライアントはデータ送信を開始できます。
初回のSYN送信時には、RTO(Retransmission Timeout)というタイマーが3秒に設定されます。この期間内にSYN+ACKが返されない場合、クライアントはSYNパケットを再送信します。複数回の再送によってもSYN+ACKが返されない場合、接続確立は失敗と判定されます。
セキュリティの重要性と脅威
ISN(Initial Sequence Number)の予測攻撃
初期シーケンス番号がランダムでない実装では、攻撃者が現在と将来のTCP接続シーケンス番号を予測し、以下の攻撃を実行できます。
- セッションハイジャック: 確立済み接続にインジェクション攻撃
- スプーフィング: 偽の送信元から有効なパケットを注入
現代の実装では、RFC 6528に基づき、接続の4タプル(送信元IP/ポート、宛先IP/ポート)と秘密鍵を組み合わせた暗号学的乱数生成が標準となっています。
SYN Flood攻撃
3-way handshakeそのものを悪用したDDoS攻撃が存在します。
攻撃の仕組み:
- 攻撃者が大量のSYNパケットをサーバーに送信
- サーバーはそれぞれに対してSYN+ACKで応答、各接続用にメモリとコネクションスロットを割り当て
- 攻撃者は最終的なACK返信を意図的に送らず、接続を完成させない
- サーバーのバックログキュー(未確立接続の保留キュー)が満杯になり、正当なクライアントの接続受け入れ不可
対策手法:
| 対策 | 説明 | 特性 |
|---|---|---|
| SYN Cookies | リソース割り当てを接続完成時まで遅延。状態情報をSYN+ACKに符号化 | 標準的で有効 |
| バックログ拡大 | 未確立接続の保留キューサイズを増加 | 低レベル攻撃への一時凌ぎ |
| レート制限 | SYN処理の時間あたり上限設定 | 攻撃流量削減 |
| SYN-ACK再送回数削減 | 待機時間短縮でリソース開放時間を促進 | リソース効率化 |
デバッグと観察
主要なツール
TCPハンドシェイクの問題を診断する際の標準的なツール。
tcpdump: パケットキャプチャの基本。フィルター例:
tcpdump -i eth0 'tcp[tcpflags] & tcp-syn != 0' # SYNパケットのみ
tcpdump -i eth0 host 192.168.1.100 and port 443 # 特定ホスト・ポートを抽出
Wireshark: GUIベースのパケット解析。TCPフラグ、シーケンス番号、ACK番号を視覚的に追跡可能。
netstat: 接続状態の確認。
netstat -an | grep SYN_RECEIVED # 半開接続数を確認(SYN flood検知)
netstat -s | grep SYN # SYN関連の統計
一般的な接続問題
接続失敗のパターン。
| 現象 | 原因 | 診断方法 |
|---|---|---|
| SYNの再送繰り返し | ネットワーク経路でSYN-ACK遮断(ファイアウォール) | tcpdump でSYN-ACKの不在確認 |
| RST(リセット) | ポート拒否またはアイドルタイムアウト | RST送信元の特定 |
| タイムアウト | サーバー無応答またはネットワーク遅延 | RTT測定、複数経路検証 |
まとめ
TCP 3-way handshakeは、インターネット通信の信頼性を支える基本メカニズムです。クライアントとサーバーが3回のパケット交換を通じて、初期シーケンス番号を同期させ、通信パラメータ(MSS、ウィンドウサイズ)を協議することで、その後の安定したデータ転送基盤を確立します。
重要な技術的特性
- 初期シーケンス番号: 暗号学的ランダム生成(セッションハイジャック対策)
- ACK機構: 相手のSeq+1応答で確実な受信確認
- パラメータ協議: MSS、ウィンドウスケーリングで最適通信実現
- 2-RTT遅延: 接続確立に最小限の往復遅延
- 攻撃耐性: SYN Cookies等の防御機構が実装
参照