SOCKSプロトコルを完全に理解する(SOCKS 4編)
SOCKSプロトコルは、ややニッチな存在です。HTTP/HTTPSプロキシに似ていますが、HTTP/HTTPSに限らず、TCP/UDP全般を中継できる点が特徴です。
しかし、ネットワークエンジニアであっても、「SOCKSはどのような仕組みで動くのか」「SOCKS 4 / SOCKS 4a / SOCKS 5にはどのような違いがあるのか」まで理解している人は、意外と多くありません。
本記事では、まずSOCKS 4に焦点を当てます。他のバージョンとの違い、通信フロー、パケット構造、制限事項を整理し、プロトコルレベルでSOCKS 4を理解することを目指します。
SOCKSの概要
SOCKSは、TCPおよびUDPの中継(プロキシ)を行うためのプロトコルです。
この記事では、SOCKSの使い方そのものではなく、プロトコルレベルの仕様や仕組みに焦点を当てます。そのため、ブラウザでのプロキシ設定方法など、SOCKSを利用する側の話は割愛します。
SOCKSには、主にSOCKS 4、SOCKS 4a、SOCKS 5の3つのバージョンがあります。まずは比較表で全体像を確認しておきましょう。
| SOCKS 4 | SOCKS 4a | SOCKS 5 | |
|---|---|---|---|
| RFC | RFC化されていない | RFC化されていない | RFC 1928 |
| バージョンID | 0x04 | 0x04 | 0x05 |
| TCPの中継 | 〇 | 〇 | 〇 |
| UDPの中継 | × | × | 〇 |
| IPv6対応 | × | × | 〇 |
| 認証 | × | × | 〇 |
| ドメイン名解決 | クライアント側のみ | サーバー側に委譲可能 | サーバー側に委譲可能 |
| 暗号化 | SOCKS自体にはなし | SOCKS自体にはなし | SOCKS自体にはなし |
SOCKS 4の仕組み
SOCKS 4の概要
今回取り上げるSOCKS 4は、SOCKSの初期仕様です。もともとはDavid Koblasによって開発され、その後、Ying-Da LeeによってSOCKS 4として拡張・普及しました。
驚くかもしれませんが、広く使われているSOCKS 4は、いまだにRFC化されていません。これは、Ying-Da Leeによる仕様文書として広まり、長く「事実上の仕様」として扱われてきました。
しかし、2025年から2026年にかけて、SOCKS 4/4aを歴史的記録としてIETF Internet-Draft化する動きがあります。長年RFC化されてこなかったこの仕様が、今後、記録としてRFCになる可能性もあります。
ここで、SOCKS 4にまつわる誤解を整理しておきましょう。
SOCKS 4は、もう古いので使われていない
そんなことはありません。SOCKS 4は互換性目的で現在も使われていますが、新規に採用するなら基本的にはSOCKS 5を選ぶ、という整理が正確です。
SOCKSはTCP/UDPに対応している
SOCKS 4/4aは、UDPに対応していません。UDPの中継が必要であれば、SOCKS 5を使用するとよいでしょう。
SOCKSはユーザー認証が可能
SOCKS 4/4aには認証機能がありません。
SOCKSは、通信を暗号化してくれる
いいえ、SOCKS自体には、通信を暗号化する機能はありません。
SOCKS 4サーバーの構築
このブログでは恒例ですが、言葉で説明するよりも実際に触ってみたほうが理解しやすいため、まずはハンズオンでSOCKS 4の動きを確認してみましょう。
SOCKS 4のプロキシサーバーを構築するには、dante-serverを使用します。dante-serverは、SOCKSプロキシサーバーを構築するための代表的なオープンソース実装で、SOCKS 4だけでなく、SOCKS 5もサポートしています(ただし、SOCKS 4aはサポートしていません)。
以下では、AWSのEC2インスタンス上にSOCKS 4プロキシサーバーを構築します。
-
インターネットからアクセス可能なLinuxのEC2インスタンスを作成します。この例では、
Ubuntu 26.04 LTSを使用します。 -
dante-serverをインストールします。Ubuntuの場合は、aptで簡単にインストールできます。1sudo apt install dante-serverインストール後、バージョンを確認します。
1danted -v2026年5月現在、
dante-serverの最新版はv1.4.4です。1Dante v1.4.4. Copyright (c) 1997 - 2024 Inferno Nettverk A/S, Norway -
構成ファイルを作成します。
1sudo mv /etc/danted.{conf,bak}いったん既存の
danted.confを退避させ、以下の内容で新たに作成します。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
logoutput: syslog internal: 0.0.0.0 port = 9580 external: ens5 socksmethod: none user.privileged: proxy user.unprivileged: nobody client pass { from: x.x.x.x/32 to: 0.0.0.0/0 log: connect disconnect } socks pass { from: 0.0.0.0/0 to: 0.0.0.0/0 command: connect udpassociate udpreply log: connect disconnect }上記の
x.x.x.xは、クライアント側のIPアドレスに置き換えてください。SOCKS 4には認証がないため、IPアドレスで制限しないと、誰でも利用できてしまいます。また、この例では
9580番ポートを使用していますが、1080など任意のポートで問題ありません。 -
dante-serverを再起動します。1 2
sudo systemctl restart danted systemctl status danted -
AWSではセキュリティグループで通信が制御されるため、
9580番ポートのTCPとUDPを許可しておきます。
SOCKS 4サーバーの動作確認
サーバーを起動できたら、クライアント側からcurlコマンドで動作確認します。
curlでは、SOCKS 4 / SOCKS 4a / SOCKS 5のプロキシサーバーを指定できます。SOCKS 4を指定する場合は、以下のどちらかの形式を使用します。この例では、danteサーバーのIPアドレスを1.1.1.1とします。
1 | |
1 | |
SOCKS 4のトラフィックの流れ
上記のcurlコマンドを実行する間に、danteサーバー(SOCKS 4サーバー)でtcpdumpなどを使ってパケットをキャプチャすると、SOCKS 4の動作を確認できます。
図にすると以下のようになります。実線はペイロードありのTCPパケットで、点線はペイロードなしのTCPパケットです。
sequenceDiagram
participant Client as クライアント
participant Socks as SOCKS 4サーバー
participant Server as HTTPサーバー
Client-->>Socks: SYN
Socks-->>Client: SYN, ACK
Client-->>Socks: ACK
Client->>Socks: CONNECTコマンド
Socks-->>Client: ACK
Socks-->>Server: SYN
Server-->>Socks: SYN, ACK
Socks-->>Server: ACK
Socks->>Client: CONNECTコマンドの応答
Client-->>Socks: ACK
rect rgb(250, 230, 230)
Client->>Socks: GET / HTTP 1.1
Socks-->>Client: ACK
Socks->>Server: GET / HTTP 1.1
Server-->>Socks: ACK
Server->>Socks: HTTP/1.1 200 OK
Socks-->>Server: ACK
Socks->>Client: HTTP/1.1 200 OK
Client-->>Socks: ACK
Client-->>Socks: FIN, ACK
Socks-->>Server: FIN, ACK
Socks-->>Client: ACK
Server-->>Socks: FIN, ACK
Socks-->>Server: ACK
Socks-->>Client: FIN, ACK
Client-->>Socks: ACK
end
SOCKS 4パケットの特徴
このシーケンス図から、SOCKS 4の基本的な動作を読み取ることができます。
SOCKS 4の動作は非常にシンプルです。まず、クライアントはSOCKS 4サーバーとの間で3-Way Handshakeを行いTCP接続を確立します。その後、クライアントはCONNECTコマンドを送信し、接続先となるターゲットサーバーを指定します。この例では、接続先はHTTPサーバーです。
CONNECTコマンドを受け取ったSOCKS 4サーバーは、指定されたターゲットサーバーとの間で新たにTCP接続を確立します。そして、接続に成功すると、クライアントへCONNECTコマンドの応答を返します。
ここまで完了すると、SOCKS 4サーバーは中継役に徹します。つまり、クライアント側から受け取ったデータをすべてそのままターゲットサーバー側へ転送し、ターゲットサーバー側から受け取ったデータをすべてそのままクライアント側へ転送します。
TCPより上のアプリケーションレイヤーから見れば、CONNECT後のSOCKS 4サーバーは、まるで本来のターゲットサーバーのように振る舞います。
一方、TCP/IPレイヤーで見ると、少し不自然な動きもあります。例えばTCP通信の終了時には、クライアント側がFIN/ACKを送信しています。それに対して通常はサーバー側もFIN/ACKを返しますが、SOCKS 4サーバーは一度ACKだけを返してから、後でFIN/ACKを返しています。おそらく、ターゲットサーバー側との通信終了処理との間にわずかなタイムラグがあるため、クライアント側を待たせないように、いったんACKを返しているのだと思います。
SOCKS 4のプロトコル
ここまで、SOCKS 4の通信全体の流れを見てきました。次に、SOCKS 4として通信の制御に使われるPDU(プロトコルデータユニット)を確認します。
上記のハンズオンで、実際にSOCKS 4のPDUが使われている箇所は2つだけです。1つは、クライアント側がSOCKS 4サーバーにCONNECTコマンドを送信する通信です。もう1つは、SOCKS 4サーバーがクライアント側にCONNECT応答を送信する通信です。
SOCKSは、HTTPやFTPなどのテキストベースのプロトコルとは異なり、バイナリプロトコルです。そのため、パケットキャプチャの中身を探しても、「CONNECT」のような文字列は出てきません。
今回の例では、これら2つの通信は、tcpdumpでキャプチャしたデータから見つけると、以下のようなバイナリデータとして表れます。
CONNECTコマンド:04 01 00 50 22 df 7c 2d 00CONNECT応答:00 5a 19 cb ac 1f 14 b6
では、これらのバイナリデータをどのように読むのかを見ていきます。
CONNECTコマンド
まず、クライアント側が送信したCONNECTコマンドの構造を見てみます。
| フィールド | バイト数 | この例での値 | 説明 |
|---|---|---|---|
| VN | 1 | 04 |
バージョン番号。SOCKS 4の場合は4で固定 |
| CD | 1 | 01 |
コマンド。01はCONNECT、02はBIND。SOCKS 4は、この2種類のコマンドのみ |
| DSTPORT | 2 | 00 50 |
接続先ポート。10進数に直すと80、HTTPポート |
| DSTIP | 4 | 22 df 8c 2d |
接続先IPアドレス。10進数に直すと34.223.124.45 |
| USERID | 可変長 | (空) | ユーザー名。認証ではなく記録用。通常使用しない |
| NULL | 1 | 00 |
可変長のUSERIDの末尾を示すためのnull |
続いて、サーバーから返されるCONNECT応答の構造を見てみます。
| フィールド | バイト数 | この例での値 | 説明 |
|---|---|---|---|
| VN | 1 | 00 |
応答番号。0で固定 |
| CD | 1 | 5a |
結果コード。10進数の90/91/92/93(16進:5a/5b/5c/5d)からなるが、90 (5a)は成功を示している |
| DSTPORT | 2 | 19 cb |
使用していないフィールドで、クライアント側がこれを無視する |
| DSTIP | 4 | ac 1f 14 b6 |
使用していないフィールドで、クライアント側がこれを無視する |
BINDコマンド
ここまでCONNECTコマンドを中心に見てきましたが、SOCKS 4にはもう1つ、BINDというコマンドがあります。
では、このBINDコマンドは何のためにあるのでしょうか。
代表的な例として、FTPのPORTモードを思い出してみましょう。FTPのPORTモード(アクティブモード)では、データ転送時にFTPクライアント側が新たなポートを開き、そのポートでサーバーからの接続を待ち受けます。つまり、要求を出しているのはクライアント側であるにもかかわらず、データ転送用のTCP接続はサーバーからクライアントへ張り返されます。この点が、通常のクライアント・サーバー型の通信と比べると少しユニークです。
このような「クライアントが待ち受け、サーバーが張り返す」動きを実現するために、SOCKS 4ではBINDコマンドが用意されています。
一方で、モダンなプロトコルでは、このようなNAT/Firewallに嫌われやすい設計はほとんど採用されません。そのため、BINDコマンドを使用する場面は多くないでしょう。
まとめ
この記事では、SOCKS 4の概要、実際の動かし方、通信フロー、パケット構造、そしてCONNECTとBINDの2つのコマンドの役割を紹介しました。
SOCKS 4は非常にシンプルなプロトコルですが、その分、後続のSOCKS 4aやSOCKS 5で何が拡張されたのかを理解するための土台になります。
今後の記事では、SOCKS 4a、SOCKS 5についても取り上げていきます。