지갑은 정말 Tor를 타는가: SOCKS5 실측 감사
지갑 네 개, 같은 SOCKS5 핸드셰이크. Electrum, Sparrow, Feather, Monero GUI가 호스트명을 어디로 넘기는지, DNS가 어디서 새는지, 직접 할 수 있는 점검 다섯 가지.
지갑의 Tor 스위치는 그것이 보내는 모든 패킷에 대한 약속이고, 그 약속을 검증하기 가장 쉬운 지점은 실제로 결정이 내려지는 한 줄, SOCKS5 요청입니다. 이 감사는 Electrum, Sparrow, Feather, Monero GUI 네 지갑의 그 요청을 읽고, 연결 대상을 기록하는 SOCKS5 프록시에서 차이를 측정했습니다. 결론은 'Tor가 된다/안 된다'가 아니라 지도입니다. 넷 중 셋은 기본 경로에서 서버 이름을 프록시에 넘기고, 각각 한 군데씩 우회하는 지점이 있습니다. 어느 것도 화면에는 보이지 않습니다. 그래서 마지막의 점검 절차가 토글 상태보다 중요합니다. (Tor 스왑은 안전한가?)
누가 이름을 푸는지를 정하는 것은 1바이트입니다. SOCKS5는 요청의 ATYP 필드에 목적지를 적습니다. 0x01은 IPv4 주소, 0x03은 도메인 이름입니다. 0x03을 보내면 이름 해석은 프록시, 즉 Tor 쪽에서 일어납니다. 0x01을 보낸다는 것은 이미 로컬에서 해석했다는 뜻이고, 그 질의는 터널이 생기기 전에 밖으로 나갔습니다. 127.0.0.1:1081에 기록형 SOCKS5 리스너를 띄우고 curl로 같은 목적지에 두 번 붙었습니다. `curl --socks5`는 `ATYP=1 HOST=172.66.147.243:80`, `curl --socks5-hostname`은 `ATYP=3 HOST=example.com:80`을 남겼습니다. 같은 프록시, 같은 목적지, 차이는 하나입니다. 앞의 경우 DNS 응답이 Tor가 개입하기 전에 이미 존재했습니다. 아래 지갑들 모두 코드에서 정확히 이 선택을 하며, 화면에는 쓰지 않습니다. (Tor 스왑은 안전한가?)
Electrum: 스스로 탐지하고, 그 탐지가 엄격합니다. 프록시를 설정하지 않은 기본 상태에서도 Electrum은 127.0.0.1의 9050, 9051, 9150 포트를 병렬로 탐색해 로컬 Tor를 자동 감지합니다. 이 탐색은 단순 TCP 연결이 아닙니다. 18바이트 SOCKS5 핸드셰이크 끝에 0xF0, 즉 Tor의 RESOLVE 확장을 넣어 0.0.0.0을 풀게 하고, 응답이 12바이트 `05 00 05 00 00 01 00 00 00 00 00 00`과 정확히 일치할 때만 인정합니다. 소스의 바이트를 직접 만든 에뮬레이터로 보내자 로그에 `CMD=0xf0 ATYP=3 HOST=0.0.0.0:0`이 찍혔습니다. Tor가 돌려주는 형태 그대로입니다. 실무적 결과는 둘입니다. RESOLVE를 구현하지 않은 일반 SOCKS5(ssh -D, dante)는 탐지에 실패하고 Electrum은 그대로 직접 연결로 남습니다 — 오류도 경고도 없습니다. 그리고 9051은 Tor의 제어 포트라서 세 탐색 중 하나는 설계상 늘 죽어 있습니다. (Tor 스왑은 안전한가?)
실제로 프록시를 통과할 때 Electrum의 주 경로는 깨끗합니다. SOCKS 계층이 호스트명을 ATYP 0x03으로 인코딩하므로 서버 이름은 미해석 상태로 프록시에 도달합니다. 예외는 같은 저장소 안에 있습니다. `dnssec.py`는 하드코딩된 8.8.8.8로 질의하고, 소스 주석에는 "FIXME this method is not using the network proxy"라고 적혀 있습니다. `contacts.py`가 결제 식별자 조회에 이 함수를 호출합니다. name@domain 형식 식별자를 쓰면 그 DNS 질의는 Tor가 돌고 프록시가 켜진 상태에서도 당신 기기에서 평문으로 나갑니다. 블록체인 연결은 아닌 좁은 경로지만, 지갑 세션을 특정하는 것은 바로 이런 좁은 경로입니다. (Tor 스왑은 안전한가?)
Sparrow: 두 개의 전송, 정반대의 답. 같은 앱이라도 선택한 서버 프로토콜에 따라 동작이 다릅니다. SSL(50002 포트)과 프록시를 쓰면 `ProxyTcpOverTlsTransport`를 만들고, 소켓 호출은 `InetSocketAddress.createUnresolved(server.getHost(), port)`입니다. 호스트명이 SOCKS 프록시로 넘어가고 로컬 리졸버는 호출되지 않습니다. 반면 평문 TCP(50001 포트)와 프록시에서는 `TcpTransport`와 `ProxySocketFactory`로 내려가고, 거기서 `createAddress`가 미해석으로 남기는 것은 .onion 호스트뿐입니다. 클리어넷 호스트는 `new InetSocketAddress(address, port)`를 거치며 생성 시점에 해석됩니다. Tor로 가는 것은 이름이 아니라 IP입니다. Sparrow의 기본 공개 서버는 SSL이라 흔한 구성은 안전한 쪽이고, 예외는 서버 목록에서 한 번의 클릭 거리에 있습니다. (Tor 스왑은 안전한가?)
Feather: 기본은 Tor지만 최초 동기화는 예외. Feather는 기본값이 proxy = Tor이고, 내장 tor 바이너리를 `--SocksPort 127.0.0.1:19450`으로 띄웁니다. 구멍은 프라이버시 레벨에 있습니다. 기본값이 '최초 동기화 제외 전부 Tor'이고, 프록시를 붙일지 결정하는 함수는 Tor 모드에서 'onion 노드만'을 반환합니다. 이 조건은 지갑이 한 번 리프레시를 마치기 전까지, 또는 체인 높이가 초기 동기화 임계값에 들기 전까지 false입니다. 즉 지갑이 평생 보내는 가장 크고 특징적인 트래픽인 최초 동기화가 클리어넷 노드로 직접 나갑니다. 동기화 후에는 프록시가 적용되고, onion 전용을 켜면 .onion 노드에 TLS를 의도적으로 끈 채 붙습니다. 그 구간은 이미 Tor가 암호화하기 때문입니다. (Tor 스왑은 안전한가?)
Monero GUI: 플래그 하나, 예상보다 넓은 범위. GUI는 `--socks5-proxy address:port`를 받고, 자체 소스에 '원격 노드 연결, 업데이트 다운로드, 가격 소스 조회'를 포함한다고 적혀 있습니다. 지갑 자체의 `--proxy` 인자는 더 좁습니다 — 데몬 연결용 socks 프록시입니다. 그 아래 SOCKS 클라이언트는 `v5_domain_connect` 요청, ATYP 0x03을 만듭니다. 데몬 호스트명이 리졸버가 아니라 프록시로 갑니다. 결과는 둘입니다. 이 설정은 일반 SOCKS5이지 Tor 스위치가 아니므로, Tor가 아닌 프록시를 가리키면 홉만 하나 늘고 익명성은 0입니다. 그리고 범위에 업데이트 확인과 가격 조회가 들어가므로, 브로드캐스트하는 순간 같은 회로에서 제3자 가격 API로도 요청이 나갑니다. (Tor 스왑은 안전한가?)
ATYP가 맞아도 보이지 않게 되지는 않습니다. 모두 0x03으로 만들면 로컬 리졸버는 배제되지만, 출구 노드는 목적지 호스트명을 여전히 보고, 클리어넷 TLS에서는 SNI도 봅니다. 둘 다 없애는 구성은 .onion 노드뿐입니다. DNS 질의도 없고, 출구가 읽을 호스트명도 없습니다. 어느 쪽이든 남는 것은 타이밍입니다. 지갑이 노드에 붙은 직후 거래가 나타나는 것은 양쪽을 보는 관찰자가 시도할 수 있는 상관관계입니다. Tor는 관측 지점을 옮길 뿐 관측을 지우지 않습니다. Tor 토글만 켜면 온체인 전송이 프라이빗해진다고 말하는 사람은 존재하지 않는 것을 팔고 있습니다. (Tor 스왑은 안전한가?)
10분이면 되는 점검 다섯 가지. (1) 지갑을 idle로 두고 `ss -tnp`. tor 프로세스가 아닌 50001, 50002, 18081, 18089 아웃바운드 연결이 있으면 안 됩니다. (2) `sudo tcpdump -ni any udp port 53`을 띄우고 지갑을 재시작하십시오. 출력되는 질의는 모두 터널에 닿지 않은 해석입니다. (3) 시스템 리졸버를 127.0.0.1 같은 싱크홀로 돌리고 재시작하십시오. 그래도 연결되면 로컬 해석을 하지 않는 것이고, 실패하면 계속 로컬에서 풀고 있던 것입니다. (4) Electrum에서 `--proxy socks5:127.0.0.1:9050`과 Tor가 아닌 SOCKS 서버를 비교해, 자동 감지가 후자를 거부하는 것을 확인하십시오. (5) Sparrow에서 프록시를 켠 채 같은 서버를 50001과 50002로 바꿔 보며 어느 쪽이 DNS 질의를 일으키는지 보십시오. (Tor 스왑은 안전한가?)
스왑에 왜 중요한가, 그리고 무엇을 고치지 못하는가. 입금 주소로 보내는 일에는 DNS 조회가 전혀 없습니다. 문자열을 붙여넣고 서명하면 끝입니다. 새는 곳이 있다면 지갑과 노드 사이의 연결이고, 그 연결은 주문 전에도 후에도 존재합니다. 둘을 분리해서 보십시오. 주문 페이지의 임무는 당신을 기록하지 않는 것 — 이 사이트는 4jyoxqdyx7dh3xfjctihpld7fgliowhimajfszcgypxash5rjqhas3yd.onion에 Tor 미러를 두고 계정을 요구하지 않습니다. 지갑의 임무는 당신을 알리지 않는 것입니다. 2026년 11월 6일 10:05 UTC 실측에서 100 USDT-TRON은 0.1420618 XMR(1 XMR당 703.9달러 상당, 현물 617.43), 1 XMR은 2개 경로로 612.157 USDT였습니다. 소액 구간의 차이는 잠금 금리 경로 때문이며 프라이버시 할증이 아닙니다. 실시간 견적이 기준입니다. SOCKS5 감사가 주는 것은 더 작고 더 쓸모 있는 지식입니다. 지갑 설정의 'Tor'는 한 요청의 한 바이트를 가리키며, 당신이 직접 확인할 수 있다는 것. (Tor 스왑은 안전한가?)