SOCKS 연결과 TLS 연결은 하는 일이 다릅니다. 먼저 SOCKS를 통해 대상으로 가는 바이트 스트림을 얻습니다. 그런 다음 그 스트림 위에서 TLS를 시작하고, 프록시가 아니라 대상 웹사이트를 식별합니다.

HTTPS 방식의 TLS 엔드포인트라면 구성은 다음과 같습니다.

use IO::Socket::Socks;
use IO::Socket::SSL;

my $target = 'example.com';
my $socket = IO::Socket::Socks->new(
    SocksVersion => 5,
    ProxyAddr => '127.0.0.1',
    ProxyPort => 1080,
    ConnectAddr => $target,
    ConnectPort => 443,
) or die $!;

IO::Socket::SSL->start_SSL(
    $socket,
    SSL_hostname => $target,
    SSL_verifycn_name => $target,
    SSL_verifycn_scheme => 'http',
    SSL_verify_mode => SSL_VERIFY_PEER,
) or die IO::Socket::SSL::errstr();

허용된 공용 프록시 경로에 있는 공용 HTTPS 대상의 경우 SOCKS5 프록시 목록이 ProxyAddr과 ProxyPort의 후보를 제공합니다. 이 조각은 전송 계층을 구성할 뿐이며, 애플리케이션은 여전히 대상이 기대하는 프로토콜을 보내고 끝나면 소켓을 닫아야 합니다.

의도한 피어 이름 유지하기

프록시 엔드포인트는 첫 번째 연결에 속합니다. TLS SNI와 호스트 이름 검증이 관련되는 대상은 example.com입니다. 그 단계에서 프록시 호스트 이름을 사용하면 잘못된 신원을 요청하거나 검증할 수 있습니다.

트러스트 스토어 구성은 또 다른 독립적 요구 사항입니다. 진단 예제에서 복사한 모든 것을 신뢰하는 콜백 대신 애플리케이션이 의도한 CA 구성을 사용하세요.

SOCKS 단계가 실패하면 TLS 매개변수를 바꿔도 해결되지 않습니다. SOCKS는 성공했는데 TLS가 실패하면 인증서와 호스트 이름 오류를 증거로 보관하고, 일반적인 프록시 장애로 취급하지 마세요.

모듈 이름도 중요합니다. IO::Socket::Socks와 IO::Socket::SSL은 생성자와 업그레이드 동작이 다릅니다. 프록시에 도달하기만 한 원시 TCP 소켓은 아직 SOCKS 대상 협상을 마치지 않았습니다.

관련 글