사전 SOCKS
프로토콜

SOCKS

gabury1고친 사람 github-actions[bot]

SOCKS 는 프록시 서버에게 목적지와의 연결을 대신 맺어 달라고 부탁하는 프로토콜입니다. 클라이언트는 어느 주소의 어느 포트로 이어 달라고만 말합니다. 연결이 이어지면 프록시는 오가는 바이트를 손대지 않고 넘깁니다. 안에 무엇이 실렸는지 보지 않으므로 웹이 아닌 연결도 넘길 수 있습니다.

쉽고 빠른 이해

SOCKS 는 프록시에게 「저 서버와 대신 이어 달라」고 부탁하는 약속입니다. 예를 들어 사내 방화벽이 바깥 데이터베이스로 가는 연결을 막고 있다고 합시다. 바깥으로 나갈 수 있는 프록시 한 대에게 대신 이어 달라고 하면 됩니다.

이게 없으면 프로그램마다 우회 수단을 따로 만들어야 합니다. 웹 프록시는 웹 요청만 알아듣기 때문입니다. SOCKS 는 내용을 안 보므로 데이터베이스 접속이든 웹이든 한 방식으로 넘깁니다.

어떻게 도는가:

  1. 프록시에 접속해 어떤 방식으로 신원을 확인할지 정합니다
  2. 목적지 주소와 포트를 알려 줍니다
  3. 프록시가 목적지와 이어지면 그 뒤로는 양쪽 바이트를 넘기기만 합니다

대가도 있습니다. 오가는 내용을 암호화하지 않습니다. 내용을 모르니 캐싱이나 주소별 차단 같은 일은 못 합니다. 거쳐 가는 구간이 하나 늘어 조금 느려집니다.

그래서 늘 SOCKS 를 고르지는 않습니다. 웹 요청만 넘길 때는 웹 프록시로 충분합니다. 프로그램마다 설정하지 않고 컴퓨터의 모든 연결을 다른 망으로 보내려면 가상 사설망을 씁니다.

상세

SOCKS 는 클라이언트가 프록시 서버를 거쳐 목적지 서버와 연결하도록 돕는 프로토콜입니다. 이름은 흔히 Socket Secure 로 풀지만 공식 약자는 아닙니다.

전화 교환원을 떠올리면 됩니다. 전화를 건 사람은 교환원에게 이을 번호만 불러 줍니다. 교환원은 두 사람의 선을 따로 잡은 채 한쪽 말을 다른 쪽으로 옮깁니다. 교환원은 누구와 누구를 이었는지만 알 뿐 말의 내용에는 끼어들지 않습니다.

연결 중간에 끼는 프록시

프로그램은 소켓을 열어 네트워크와 바이트를 주고받습니다. 소켓은 운영체제가 내주는 네트워크 창구입니다. 프로그램은 여기에 상대 주소를 넘겨 연결을 엽니다. 그 뒤로 연결에 바이트를 읽고 씁니다.

평소에는 클라이언트가 목적지 서버와 TCP(Transmission Control Protocol, 전송 제어 프로토콜) 연결을 곧장 맺습니다. SOCKS 를 쓰면 연결이 둘로 나뉩니다. 클라이언트는 프록시와 연결을 맺습니다. 프록시는 목적지와 연결을 따로 맺습니다.

flowchart TD
    subgraph IN["방화벽 안쪽"]
        C["클라이언트"]
    end
    P["SOCKS 프록시"]
    subgraph OUT["바깥 망"]
        S["목적지 서버"]
    end
    C -->|"연결 1 · 클라이언트가 맺음"| P
    P -->|"연결 2 · 프록시가 맺음"| S

그림의 두 연결은 서로 다른 TCP 연결입니다. 프록시는 연결 1 로 들어온 바이트를 연결 2 로 내보냅니다. 거꾸로 오는 바이트도 같은 식으로 넘깁니다.

목적지 서버가 보기에는 프록시가 접속해 온 것입니다. 서버가 보는 출발 IP 주소(Internet Protocol address, 인터넷 프로토콜 주소)는 프록시의 것입니다. 그래서 방화벽이 바깥 연결을 막은 망에서도 프록시 한 대만 바깥으로 열어 두면 안쪽 프로그램들이 밖으로 나갈 수 있습니다.

HTTP 프록시와 다른 점

HTTP(HyperText Transfer Protocol, 하이퍼텍스트 전송 프로토콜)는 웹 요청과 응답을 주고받는 프로토콜입니다. 흔히 웹 프록시라고 부르는 HTTP 프록시는 이 웹 요청을 다루는 프록시입니다.

HTTP 프록시는 오가는 HTTP 요청을 읽습니다. 요청에 적힌 주소를 보고 넘길지 정합니다. 응답을 저장해 두었다가 다음 요청에 내주기도 합니다.

SOCKS 프록시는 연결을 열 때 받은 목적지 주소와 포트만 압니다. 그 뒤에 오가는 바이트는 읽지 않습니다. 그래서 어떤 프로토콜이 실려 있든 넘길 수 있습니다.

판에 따라서는 UDP(User Datagram Protocol, 사용자 데이터그램 프로토콜)도 넘깁니다. UDP 는 연결을 맺지 않고 데이터그램이라는 덩어리를 하나씩 던지는 프로토콜입니다. 두 프록시를 나란히 놓으면 아래와 같습니다.

HTTP 프록시 SOCKS 프록시
읽는 것 HTTP 요청과 응답 목적지 주소와 포트
넘길 수 있는 연결 HTTP TCP 연결 전부, 판에 따라 UDP 까지
캐싱 할 수 있음 못 함
판단의 단위 요청 하나 연결 하나

HTTP 프록시도 CONNECT 메서드로 요청을 받으면 SOCKS 처럼 바이트를 넘기는 터널을 엽니다. CONNECT 메서드는 「이 주소와 포트로 연결을 이어 달라」는 HTTP 요청입니다. 이 경우에는 HTTP 프록시도 안의 내용을 읽지 않습니다.

그래도 두 방식에는 차이가 남습니다. CONNECT 요청은 TCP 연결만 열 뿐 UDP 는 넘기지 못합니다. 클라이언트가 HTTP 요청을 만들 줄 알아야 한다는 조건도 붙습니다. SOCKS 에는 이 조건이 없어서 HTTP 를 모르는 프로그램도 그대로 씁니다.

연결 하나를 여는 두 왕복

이 소절은 지금 흔히 쓰는 다섯 번째 판, SOCKS5 로 연결 하나를 여는 과정을 봅니다. 클라이언트와 프록시는 두 번 묻고 답합니다. 첫 왕복에서 신원 확인 방식, 곧 인증 방식을 정합니다. 둘째 왕복에서 목적지를 알립니다. 프록시는 목적지와 연결을 맺어 본 뒤에 둘째 답을 보냅니다.

sequenceDiagram
    participant C as 클라이언트
    participant P as 프록시
    participant S as 목적지
    C->>P: 인사 · 쓸 수 있는 인증 방식 목록
    P-->>C: 고른 인증 방식
    C->>P: 연결 요청 · example.com 443
    P->>S: TCP 연결을 맺는다
    P-->>C: 결과 · 성공
    Note over C,S: 이 뒤로 프록시는 바이트를 넘기기만 한다

첫 메시지는 인사입니다. 판 번호, 인증 방식의 개수, 인증 방식 목록이 차례로 한 바이트씩 들어갑니다. 이 절의 바이트 값은 모두 한 바이트를 16진수 두 자리로 적습니다. 아래는 「인증 없이 쓰겠다」 하나만 내민 인사입니다.

05        // 판 번호 5
01        // 인증 방식 1개
00        // 인증 없음

프록시는 목록에서 하나를 골라 두 바이트로 답합니다. 판 번호와 고른 방식입니다.

05        // 판 번호 5
00        // 인증 없음으로 정함

인증 방식 값은 몇 가지가 정해져 있습니다. 백엔드에서 자주 만나는 것은 아래 셋입니다.

값 뜻
00 인증 없음
02 사용자 이름과 비밀번호
FF 받아들일 방식이 없음. 프록시가 연결을 끊는다

사용자 이름과 비밀번호를 고르면 둘째 왕복 전에 왕복이 하나 더 끼어듭니다. 클라이언트가 이름과 비밀번호를 보냅니다. 프록시가 맞는지 답합니다.

둘째 왕복은 연결 요청입니다. 클라이언트가 명령과 목적지를 보냅니다. 명령은 프록시에게 무엇을 해 달라는지 고르는 한 바이트 값입니다. 보통은 목적지로 연결을 맺으라는 CONNECT 를 보냅니다.

이 CONNECT 는 앞에서 본 HTTP 의 CONNECT 메서드와 이름만 같습니다. HTTP 요청이 아니라 SOCKS 메시지 안에 든 값입니다. 아래는 example.com 의 443 포트로 이어 달라는 요청입니다.

05             // 판 번호 5
01             // 명령: CONNECT
00             // 예약 칸, 늘 0
03             // 주소 종류: 도메인 이름
0b             // 이름 길이 11
example.com    // 이름 11바이트
01 bb          // 포트 443

IP 주소에는 두 판이 함께 쓰입니다. IPv4와 IPv6는 IP 의 네 번째 판과 여섯 번째 판(Internet Protocol version 4·6)입니다. IPv4 주소는 4바이트, IPv6 주소는 16바이트입니다.

요청 안의 주소 종류 바이트는 뒤따르는 주소를 어떻게 읽을지 정합니다. 값마다 뒤에 오는 것이 다릅니다.

값 뒤따르는 주소
01 IPv4 주소 4바이트
03 길이 한 바이트, 이어서 그 길이만큼의 도메인 이름
04 IPv6 주소 16바이트

포트는 두 바이트에 높은 바이트부터 적습니다. 01 bb 를 16진수 한 수 01bb 로 읽으면 443 입니다.

프록시는 목적지와 연결을 맺어 본 뒤 결과를 답합니다. 응답의 모양은 요청과 같습니다. 명령 바이트 대신 결과 코드가 들어갑니다.

05             // 판 번호 5
00             // 결과: 성공
00             // 예약 칸
01             // 주소 종류: IPv4
0a 00 00 05    // 10.0.0.5
c3 50          // 포트 50000

응답 끝의 주소와 포트는 프록시가 목적지 쪽 연결에 쓴 것입니다. 목적지 서버는 이 주소와 포트에서 접속이 왔다고 봅니다.

결과가 성공이면 클라이언트와 프록시 사이의 연결(그림의 연결 1)은 이제 목적지로 이어진 통로가 됩니다. 클라이언트는 그 위에서 원래 하려던 대화를 시작합니다.

결과 코드가 성공이 아니면 프록시가 연결을 닫습니다. 클라이언트 라이브러리는 이 코드를 오류 메시지로 바꿔 보여 줍니다. 자주 보는 코드는 아래와 같습니다.

코드 뜻 흔한 까닭
01 프록시 쪽 일반 실패 프록시 내부 오류
02 규칙이 허용하지 않음 프록시가 그 목적지를 막아 두었다
04 호스트에 닿을 수 없음 프록시에서 목적지로 가는 길이 없다
05 연결 거부 목적지 포트에 듣고 있는 프로그램이 없다
07 명령을 지원하지 않음 프록시가 그 명령을 구현하지 않았다

세 가지 명령

연결 요청의 명령 바이트는 세 값 가운데 하나입니다. 대부분의 연결은 첫째 명령만 씁니다. 나머지 둘은 특수한 경우를 위한 것입니다.

값 명령 하는 일
01 CONNECT 프록시가 목적지로 연결을 맺는다
02 BIND 프록시가 포트를 열고 목적지가 접속해 오기를 기다린다
03 UDP ASSOCIATE 프록시가 UDP 로 오가는 데이터를 중계한다

BIND 는 서버가 거꾸로 클라이언트에게 접속해 오는 프로토콜을 위한 명령입니다. FTP(File Transfer Protocol, 파일 전송 프로토콜)가 대표입니다. 클라이언트는 방화벽 안에 있어서 바깥 서버가 직접 접속해 올 수 없습니다. 그래서 프록시가 대신 포트를 열고 기다립니다.

UDP ASSOCIATE 는 UDP 를 넘기는 명령입니다. 이 명령을 보내면 프록시가 중계용 UDP 포트를 알려 줍니다. 클라이언트는 그 포트로 데이터그램을 보냅니다.

UDP 에는 연결이 없어서 프록시가 연결을 보고 목적지를 알 수 없습니다. 그래서 클라이언트는 데이터그램마다 앞에 목적지 주소를 적은 헤더를 붙입니다. 헤더는 실어 보낼 데이터 앞에 붙는 안내 정보입니다. 프록시는 이 헤더를 보고 데이터그램을 목적지로 넘깁니다.

이 중계는 명령을 보낸 TCP 연결에 묶여 있습니다. 그 TCP 연결이 끊기면 UDP 중계도 끝납니다.

SOCKS4 와 SOCKS5

SOCKS 에는 판이 여럿 있습니다. 지금 만나는 것은 SOCKS4, 그 확장인 SOCKS4a, 그리고 SOCKS5 입니다. 아래 표는 판마다 무엇을 할 수 있는지 견줍니다.

판 목적지 주소 인증 방식 고르기 UDP
SOCKS4 IPv4 주소만 없음 못 넘김
SOCKS4a IPv4 주소 또는 도메인 이름 없음 못 넘김
SOCKS5 IPv4 · IPv6 · 도메인 이름 있음 넘김

SOCKS5 는 SOCKS4 를 넓힌 판입니다. 그런데 메시지 모양이 달라 서로 알아듣지 못합니다. 그래서 클라이언트 설정 화면이 프록시 주소와 함께 판을 따로 묻습니다.

이름을 누가 풀어 주나

DNS(Domain Name System, 도메인 이름 시스템)는 example.com 같은 도메인 이름을 IP 주소로 바꿔 주는 시스템입니다. SOCKS5 클라이언트는 목적지를 두 방식으로 알릴 수 있습니다. 이름을 직접 풀어 IP 주소로 보내거나, 이름을 보내 프록시에게 풀게 합니다.

클라이언트가 직접 풀면 DNS 질의가 프록시를 거치지 않고 나갑니다. 어느 사이트에 접속하는지가 내 쪽 망의 DNS 서버에 그대로 드러납니다. 프록시를 거쳐 숨기려던 목적지를 그 서버와 망 관리자가 볼 수 있습니다. 이것을 DNS 누출이라고 부릅니다.

곤란한 일이 하나 더 있습니다. 프록시 쪽 망에서만 풀리는 사내 이름은 아예 못 찾기도 합니다.

도구가 이 선택을 설정으로 드러내기도 합니다. curl 에서 socks5:// 로 적으면 curl 이 이름을 직접 풉니다. socks5h:// 로 적으면 이름을 프록시에게 보냅니다. h 는 호스트 이름을 프록시가 푼다는 표시입니다.

SSH 로 여는 SOCKS 프록시

백엔드 개발자가 SOCKS 를 가장 자주 만나는 경우는 SSH(Secure Shell, 보안 셸)입니다. SSH 는 원격 서버에 암호화된 연결로 접속하는 프로토콜입니다.

SSH 에는 포트 포워딩이라는 기능이 있습니다. 내 컴퓨터의 포트 하나를 SSH 연결 너머의 목적지 하나에 잇는 기능입니다. 목적지가 하나로 묶이므로 목적지가 늘 때마다 따로 설정해야 합니다.

동적 포트 포워딩은 이 불편을 덜어 줍니다. 내 컴퓨터에 SOCKS 프록시를 엽니다. 목적지는 연결마다 SOCKS 요청으로 고릅니다.

배스천 호스트는 내부망 앞에 세워 둔 접속용 서버입니다. 줄여서 배스천이라고 부릅니다. ssh -D 1080 사용자@배스천 을 실행하면 내 컴퓨터의 1080 포트에 SOCKS 프록시가 생깁니다. 1080 은 SOCKS 가 관례로 쓰는 포트 번호입니다.

이 프록시로 보낸 연결은 SSH 연결 안을 지나 배스천에 닿습니다. 배스천의 SSH 서버가 거기서 목적지와 연결을 맺습니다.

flowchart TD
    A["내 컴퓨터의 프로그램"] -->|"SOCKS · 암호화 없음"| B["내 컴퓨터의 SSH 클라이언트 · 1080 포트"]
    B -->|"SSH 연결 · 암호화"| C["배스천의 SSH 서버"]
    C -->|"TCP · 암호화 없음"| D["내부망의 데이터베이스"]

그림에서 인터넷을 건너는 구간은 SSH 연결입니다. 이 구간은 SSH 가 암호화합니다. 양 끝의 짧은 구간은 SOCKS 나 TCP 로 오가므로 암호화되지 않습니다.

프로그램은 SOCKS 프록시만 가리키면 내부망 어느 주소로든 붙을 수 있습니다. 목적지가 늘어도 SSH 설정을 고칠 일이 없습니다. Java 프로그램이라면 socksProxyHost 와 socksProxyPort 시스템 속성으로 이 프록시를 가리킵니다.

익명 통신망 Tor 도 클라이언트에게 SOCKS 창구를 엽니다. 프로그램은 SOCKS 프록시에 연결하듯 Tor 에 연결을 맡깁니다.

대가와 쓰지 않는 경우

SOCKS 는 오가는 바이트를 암호화하지 않습니다. 사용자 이름과 비밀번호로 인증해도 그 값이 평문으로 오갑니다. 내용을 보호하려면 안에 TLS(Transport Layer Security, 전송 계층 보안)를 싣거나 앞의 예처럼 SSH 로 감쌉니다.

프록시는 내용을 모르므로 연결 단위로만 판단합니다. 목적지 주소와 포트로 막을 수는 있습니다. 요청 경로별로 막거나 응답을 캐싱하지는 못합니다.

목적지 서버는 원래 클라이언트의 주소를 모릅니다. HTTP 프록시는 원래 주소를 요청 헤더에 적어 넘길 수 있습니다. SOCKS 프록시는 내용을 건드리지 않으므로 그런 헤더를 붙이지 않습니다.

연결을 열 때마다 프록시와 두 번 더 왕복합니다. 오가는 바이트도 프록시를 한 번 더 거칩니다. 지연이 그만큼 늘어납니다.

그래서 쓰지 않는 경우도 갈립니다. 웹 요청만 넘기고 캐싱이나 주소별 차단이 필요하면 HTTP 프록시를 씁니다. 프로그램마다 설정할 필요 없이 컴퓨터의 모든 연결을 다른 망으로 보내야 하면 VPN(Virtual Private Network, 가상 사설망)을 씁니다. SOCKS 는 프록시 설정을 받는 프로그램에만 걸립니다.

관련 항목

SOCKS 가 속하는 상위 분류

프록시 · 프로토콜 · 네트워크 · 터널링 · 세션 계층 · OSI 모형

SOCKS 연결을 싣는 전송 계층과 주소

TCP · UDP · 소켓 · 포트 · IP 주소 · IPv4 · IPv6

SOCKS 의 판과 인증 방식

SOCKS4 · SOCKS4a · GSSAPI · Kerberos · 사용자 인증

SOCKS 와 같은 일을 두고 겨루는 수단

HTTP 프록시 · CONNECT 메서드 · VPN · SSH 터널 · 포트 포워딩 · 리버스 프록시

SOCKS 프록시를 열거나 SOCKS 로 연결을 넘기는 도구

SSH · OpenSSH · 동적 포트 포워딩 · Tor · Shadowsocks · proxychains · Dante

SOCKS 프록시를 가리키는 클라이언트 설정

curl · 웹 브라우저 · 프록시 자동 설정 · ALL_PROXY · JVM 시스템 속성

SOCKS 로 넘는 네트워크 경계

방화벽 · 배스천 호스트 · NAT · 사설망 · DMZ

SOCKS 에서 새는 정보와 그걸 막는 수단

DNS · DNS 누출 · 평문 전송 · 중간자 공격 · TLS · 암호화

SOCKS 로 넘기는 응용 프로토콜

HTTP · HTTPS · FTP · SMTP · 데이터베이스

다른 이름: SOCKS 프록시 · SOCKS5