コンテンツにスキップ

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プロキシサーバーを構築します。

  1. インターネットからアクセス可能なLinuxのEC2インスタンスを作成します。この例では、Ubuntu 26.04 LTSを使用します。

  2. dante-serverをインストールします。Ubuntuの場合は、aptで簡単にインストールできます。

    1
    sudo apt install dante-server
    

    インストール後、バージョンを確認します。

    1
    danted -v
    

    2026年5月現在、dante-serverの最新版はv1.4.4です。

    1
    Dante v1.4.4.  Copyright (c) 1997 - 2024 Inferno Nettverk A/S, Norway
    
  3. 構成ファイルを作成します。

    1
    sudo 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など任意のポートで問題ありません。

  4. dante-serverを再起動します。

    1
    2
    sudo systemctl restart danted
    systemctl status danted
    
  5. AWSではセキュリティグループで通信が制御されるため、9580番ポートのTCPとUDPを許可しておきます。

SOCKS 4サーバーの動作確認

サーバーを起動できたら、クライアント側からcurlコマンドで動作確認します。

curlでは、SOCKS 4 / SOCKS 4a / SOCKS 5のプロキシサーバーを指定できます。SOCKS 4を指定する場合は、以下のどちらかの形式を使用します。この例では、danteサーバーのIPアドレスを1.1.1.1とします。

1
curl -x socks4://1.1.1.1:9580 http://neverssl.com
1
curl --socks4 1.1.1.1:9580 http://neverssl.com

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 00
  • CONNECT応答: 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の概要、実際の動かし方、通信フロー、パケット構造、そしてCONNECTBINDの2つのコマンドの役割を紹介しました。

SOCKS 4は非常にシンプルなプロトコルですが、その分、後続のSOCKS 4aやSOCKS 5で何が拡張されたのかを理解するための土台になります。

今後の記事では、SOCKS 4a、SOCKS 5についても取り上げていきます。