Solução de problemas com SSL/TLS ao usar proxy: guia completo
Erros SSL/TLS são um dos problemas mais frequentes ao trabalhar através de servidores proxy. A configuração incorreta leva a falhas de conexão, vazamento de dados e impossibilidade de fazer parsing de recursos protegidos. Neste guia, analisaremos as causas dos erros e formas de resolvê-los com exemplos específicos de código.
Como funciona SSL/TLS através de proxy
Antes de resolver problemas, é importante entender o mecanismo de funcionamento de conexões protegidas através de proxy. Existem duas abordagens fundamentalmente diferentes para fazer proxy de tráfego HTTPS, e cada uma tem suas próprias características, vantagens e possíveis pontos de falha.
Tunelamento transparente (CONNECT)
Ao usar o método CONNECT, o servidor proxy cria um túnel TCP entre o cliente e o servidor de destino. O proxy não vê o conteúdo do tráfego — ele simplesmente transmite bytes criptografados em ambas as direções. Este é o método mais seguro e comum para trabalhar com HTTPS através de proxy.
O processo de estabelecimento de conexão funciona da seguinte forma: o cliente envia uma solicitação CONNECT para o proxy especificando o host e porta de destino. O proxy estabelece uma conexão TCP com o servidor de destino e retorna ao cliente uma resposta 200 Connection Established. Depois disso, o cliente realiza o handshake TLS diretamente com o servidor de destino através do túnel estabelecido.
# Esquema de túnel CONNECT
Cliente → Proxy: CONNECT example.com:443 HTTP/1.1
Proxy → Servidor: [Conexão TCP na porta 443]
Proxy → Cliente: HTTP/1.1 200 Connection Established
Cliente ↔ Servidor: [Handshake TLS através do túnel]
Cliente ↔ Servidor: [Tráfego criptografado]
Proxy MITM (interceptação)
Alguns proxies (especialmente corporativos) usam a técnica Man-in-the-Middle: eles terminam a conexão TLS do seu lado, descriptografam o tráfego e depois estabelecem uma nova conexão TLS com o servidor de destino. Para isso, o proxy gera seu próprio certificado para cada domínio, assinado pelo certificado raiz do proxy.
Esta abordagem permite inspecionar e filtrar tráfego HTTPS, mas requer a instalação do certificado raiz do proxy no armazenamento de certificados confiáveis do cliente. É aqui que surgem a maioria dos problemas com SSL/TLS ao trabalhar através de redes corporativas.
Erros típicos e suas causas
Vamos considerar os erros SSL/TLS mais comuns ao trabalhar através de proxy. Compreender a causa de cada erro é a chave para resolver o problema rapidamente.
SSL: CERTIFICATE_VERIFY_FAILED
Este erro significa que o cliente não consegue verificar a autenticidade do certificado do servidor. Pode haver várias causas: o proxy usa inspeção MITM com certificado raiz não instalado, o certificado do servidor de destino expirou ou é auto-assinado, ou falta um certificado intermediário na cadeia.
# Python - mensagem de erro típica
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED]
certificate verify failed: unable to get local issuer certificate
# Node.js
Error: unable to verify the first certificate
# cURL
curl: (60) SSL certificate problem: unable to get local issuer certificate
SSL: WRONG_VERSION_NUMBER
O erro ocorre quando o cliente tenta estabelecer uma conexão TLS, mas recebe uma resposta em formato inesperado. Na maioria das vezes, isso acontece quando o proxy não suporta o método CONNECT e tenta processar a solicitação HTTPS como HTTP comum. Também pode ser causado por porta ou protocolo incorretos.
TLSV1_ALERT_PROTOCOL_VERSION
O servidor rejeita a conexão devido a uma versão incompatível do protocolo TLS. Os servidores modernos desabilitam versões antigas do TLS 1.0 e TLS 1.1 por razões de segurança. Se o proxy ou cliente estiverem configurados para usar versões antigas, a conexão será rejeitada.
SSLV3_ALERT_HANDSHAKE_FAILURE
O cliente e servidor não conseguiram negociar os parâmetros de criptografia. Isso pode ocorrer devido à falta de cipher suites comuns, problemas com SNI (Server Name Indication) ou incompatibilidade de bibliotecas criptográficas.
| Erro | Causa provável | Solução |
|---|---|---|
CERTIFICATE_VERIFY_FAILED |
Proxy MITM, certificado expirado | Instalar certificado raiz do proxy |
WRONG_VERSION_NUMBER |
HTTP em vez de HTTPS, sem suporte CONNECT | Verificar configurações de proxy e protocolo |
PROTOCOL_VERSION |
Versão TLS desatualizada | Atualizar versão mínima de TLS |
HANDSHAKE_FAILURE |
Cipher suites incompatíveis | Configurar lista de cifras |
Problemas com certificados
Certificados são a fonte mais frequente de problemas ao trabalhar através de proxy. Vamos considerar os principais cenários e formas de resolvê-los.
Instalação do certificado raiz do proxy
Se o proxy usar inspeção MITM, é necessário adicionar seu certificado raiz ao armazenamento de certificados confiáveis. O processo depende do sistema operacional e da linguagem de programação usada.
# Linux: adicionar certificado ao armazenamento do sistema
sudo cp proxy-ca.crt /usr/local/share/ca-certificates/
sudo update-ca-certificates
# macOS: adicionar ao Keychain
sudo security add-trusted-cert -d -r trustRoot \
-k /Library/Keychains/System.keychain proxy-ca.crt
# Windows PowerShell (como administrador)
Import-Certificate -FilePath "proxy-ca.crt" `
-CertStoreLocation Cert:\LocalMachine\Root
Especificação do certificado no código
Às vezes é mais conveniente especificar o caminho para o certificado diretamente no código, especialmente ao trabalhar em contêineres ou em servidores sem acesso root. Isso permite evitar modificações nas configurações do sistema.
# Python: especificar CA-bundle personalizado
import requests
# Forma 1: especificar caminho para arquivo de certificado
response = requests.get(
'https://example.com',
proxies={'https': 'http://proxy:8080'},
verify='/path/to/proxy-ca.crt'
)
# Forma 2: combinar com certificados do sistema
import certifi
import ssl
# Criar arquivo de certificados combinado
with open('combined-ca.crt', 'w') as combined:
with open(certifi.where()) as system_certs:
combined.write(system_certs.read())
with open('proxy-ca.crt') as proxy_cert:
combined.write(proxy_cert.read())
response = requests.get(url, verify='combined-ca.crt')
Desabilitação da verificação de certificados
Em alguns casos (apenas para testes e depuração!) pode ser necessário desabilitar a verificação de certificados. Isso não é seguro e não deve ser usado em produção, pois torna a conexão vulnerável a ataques MITM.
⚠️ Aviso: Desabilitar a verificação de certificados torna a conexão vulnerável a interceptação. Use apenas para depuração em ambiente isolado, nunca em produção!
# Python - APENAS para depuração!
import requests
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
response = requests.get(
'https://example.com',
proxies={'https': 'http://proxy:8080'},
verify=False # Não seguro!
)
Configuração de túneis CONNECT
A configuração correta do túnel CONNECT é a chave para o funcionamento estável de HTTPS através de proxy. Vamos considerar as características de configuração para diferentes cenários de uso.
Verificação de suporte CONNECT
Nem todos os proxies suportam o método CONNECT. Proxies HTTP podem ser configurados apenas para fazer proxy de tráfego HTTP. Antes de usar, certifique-se de que o proxy suporta tunelamento HTTPS.
# Verificar suporte CONNECT via netcat/telnet
echo -e "CONNECT example.com:443 HTTP/1.1\r\nHost: example.com:443\r\n\r\n" | \
nc proxy.example.com 8080
# Resposta esperada em caso de sucesso:
# HTTP/1.1 200 Connection Established
# Possíveis respostas em caso de erro:
# HTTP/1.1 403 Forbidden - CONNECT proibido
# HTTP/1.1 405 Method Not Allowed - método não suportado
Autenticação em solicitações CONNECT
Ao usar um proxy com autenticação, é importante transmitir corretamente as credenciais. O cabeçalho Proxy-Authorization deve estar presente na solicitação CONNECT.
# Python: autenticação via URL
import requests
proxy_url = 'http://username:password@proxy.example.com:8080'
response = requests.get(
'https://target.com',
proxies={'https': proxy_url}
)
# Python: autenticação via cabeçalhos (para casos complexos)
import base64
credentials = base64.b64encode(b'username:password').decode('ascii')
session = requests.Session()
session.headers['Proxy-Authorization'] = f'Basic {credentials}'
session.proxies = {'https': 'http://proxy.example.com:8080'}
response = session.get('https://target.com')
Trabalho com proxy SOCKS5
Proxies SOCKS5 funcionam em um nível mais baixo e não requerem tratamento especial de HTTPS. Eles simplesmente tunelizam conexões TCP, o que os torna ideais para trabalhar com qualquer protocolo.
# Python com PySocks
import requests
proxies = {
'http': 'socks5h://user:pass@proxy:1080',
'https': 'socks5h://user:pass@proxy:1080'
}
# socks5h - resolução DNS através do proxy
# socks5 - resolução DNS local
response = requests.get('https://example.com', proxies=proxies)
Soluções para diferentes linguagens de programação
Cada linguagem e biblioteca têm suas próprias características ao trabalhar com SSL/TLS através de proxy. Vamos considerar as opções mais comuns com exemplos completos de código.
Python (requests + urllib3)
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.ssl_ import create_urllib3_context
import ssl
class TLSAdapter(HTTPAdapter):
"""Adaptador com parâmetros TLS configuráveis"""
def __init__(self, ssl_context=None, **kwargs):
self.ssl_context = ssl_context
super().__init__(**kwargs)
def init_poolmanager(self, *args, **kwargs):
if self.ssl_context:
kwargs['ssl_context'] = self.ssl_context
return super().init_poolmanager(*args, **kwargs)
# Criar contexto com configurações modernas
ctx = create_urllib3_context()
ctx.minimum_version = ssl.TLSVersion.TLSv1_2
ctx.set_ciphers('ECDHE+AESGCM:DHE+AESGCM:ECDHE+CHACHA20')
# Carregar certificado adicional
ctx.load_verify_locations('proxy-ca.crt')
session = requests.Session()
session.mount('https://', TLSAdapter(ssl_context=ctx))
session.proxies = {
'http': 'http://proxy:8080',
'https': 'http://proxy:8080'
}
response = session.get('https://example.com')
print(response.status_code)
Node.js (axios + https-proxy-agent)
const axios = require('axios');
const { HttpsProxyAgent } = require('https-proxy-agent');
const fs = require('fs');
const https = require('https');
// Carregar certificado CA adicional
const customCA = fs.readFileSync('proxy-ca.crt');
const agent = new HttpsProxyAgent({
host: 'proxy.example.com',
port: 8080,
auth: 'username:password',
ca: customCA,
rejectUnauthorized: true // Não desabilite em produção!
});
const client = axios.create({
httpsAgent: agent,
proxy: false // Importante: desabilitar proxy integrado do axios
});
async function fetchData() {
try {
const response = await client.get('https://example.com');
console.log(response.data);
} catch (error) {
if (error.code === 'UNABLE_TO_VERIFY_LEAF_SIGNATURE') {
console.error('Problema com certificado:', error.message);
}
throw error;
}
}
fetchData();
Go (net/http)
package main
import (
"crypto/tls"
"crypto/x509"
"io/ioutil"
"net/http"
"net/url"
"log"
)
func main() {
// Carregar certificado adicional
caCert, err := ioutil.ReadFile("proxy-ca.crt")
if err != nil {
log.Fatal(err)
}
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)
// Configurar TLS
tlsConfig := &tls.Config{
RootCAs: caCertPool,
MinVersion: tls.VersionTLS12,
}
// Configurar proxy
proxyURL, _ := url.Parse("http://user:pass@proxy:8080")
transport := &http.Transport{
Proxy: http.ProxyURL(proxyURL),
TLSClientConfig: tlsConfig,
}
client := &http.Client{Transport: transport}
resp, err := client.Get("https://example.com")
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
log.Printf("Status: %s", resp.Status)
}
cURL (linha de comando)
# Solicitação básica através de proxy
curl -x http://proxy:8080 https://example.com
# Com autenticação
curl -x http://proxy:8080 -U username:password https://example.com
# Com especificação de certificado CA
curl -x http://proxy:8080 --cacert proxy-ca.crt https://example.com
# Forçar uso de TLS 1.2+
curl -x http://proxy:8080 --tlsv1.2 https://example.com
# Depuração de handshake TLS
curl -x http://proxy:8080 -v --trace-ascii - https://example.com 2>&1 | \
grep -E "(SSL|TLS|certificate)"
Detecção e bypass de inspeção MITM
Algumas redes (corporativas, Wi-Fi público) usam inspeção MITM transparente de tráfego HTTPS. Isso pode causar problemas com certificate pinning e prejudicar o funcionamento de aplicações que esperam certificados específicos.
Detecção de proxy MITM
import ssl
import socket
def check_certificate_issuer(hostname, port=443):
"""Verifica quem emitiu o certificado do site"""
context = ssl.create_default_context()
with socket.create_connection((hostname, port)) as sock:
with context.wrap_socket(sock, server_hostname=hostname) as ssock:
cert = ssock.getpeercert()
issuer = dict(x[0] for x in cert['issuer'])
subject = dict(x[0] for x in cert['subject'])
print(f"Subject: {subject.get('commonName')}")
print(f"Issuer: {issuer.get('commonName')}")
print(f"Organization: {issuer.get('organizationName')}")
# Proxies MITM corporativos conhecidos
mitm_indicators = [
'Zscaler', 'BlueCoat', 'Symantec', 'Fortinet',
'Palo Alto', 'McAfee', 'Cisco', 'Corporate'
]
issuer_str = str(issuer)
for indicator in mitm_indicators:
if indicator.lower() in issuer_str.lower():
print(f"⚠️ Proxy MITM detectado: {indicator}")
return True
return False
# Verificação
check_certificate_issuer('google.com')
Trabalho em condições de MITM
Se um proxy MITM for detectado, existem várias estratégias de trabalho. Você pode instalar o certificado raiz do proxy (se for uma rede corporativa confiável), usar portas ou protocolos alternativos, ou aplicar VPN para contornar a inspeção.
# Obter certificado do proxy MITM para instalação
openssl s_client -connect example.com:443 -proxy proxy:8080 \
-showcerts 2>/dev/null | \
openssl x509 -outform PEM > mitm-cert.pem
# Visualizar informações do certificado
openssl x509 -in mitm-cert.pem -text -noout | head -20
Ferramentas de diagnóstico
O diagnóstico correto é metade da solução do problema. Vamos considerar ferramentas e métodos para depuração de conexões SSL/TLS através de proxy.
OpenSSL para diagnóstico
# Verificar conexão através de proxy
openssl s_client -connect example.com:443 \
-proxy proxy.example.com:8080 \
-servername example.com
# Verificar cadeia de certificados
openssl s_client -connect example.com:443 \
-proxy proxy:8080 \
-showcerts 2>/dev/null | \
grep -E "(Certificate chain|s:|i:)"
# Testar versão específica de TLS
openssl s_client -connect example.com:443 \
-proxy proxy:8080 \
-tls1_2
# Verificar cipher suites suportadas
openssl s_client -connect example.com:443 \
-proxy proxy:8080 \
-cipher 'ECDHE-RSA-AES256-GCM-SHA384'
Script Python para diagnóstico
import ssl
import socket
import requests
from urllib.parse import urlparse
def diagnose_ssl_proxy(target_url, proxy_url):
"""Diagnóstico abrangente de SSL através de proxy"""
print(f"🔍 Diagnóstico: {target_url}")
print(f"📡 Proxy: {proxy_url}\n")
# 1. Verificar disponibilidade do proxy
proxy = urlparse(proxy_url)
try:
sock = socket.create_connection(
(proxy.hostname, proxy.port),
timeout=10
)
sock.close()
print("✅ Proxy disponível")
except Exception as e:
print(f"❌ Proxy indisponível: {e}")
return
# 2. Verificar método CONNECT
try:
response = requests.get(
target_url,
proxies={'https': proxy_url},
timeout=15
)
print(f"✅ Conexão HTTPS bem-sucedida: {response.status_code}")
except requests.exceptions.SSLError as e:
print(f"❌ Erro SSL (tentativa): {e}")
# Tentar com verificação desabilitada para diagnóstico
try:
response = requests.get(
target_url,
proxies={'https': proxy_url},
verify=False,
timeout=15
)
print("⚠️ Conexão funciona sem verificação de certificado")
print(" Provável problema com certificado CA do proxy")
except Exception as e2:
print(f"❌ Não funciona nem sem verificação: {e2}")
except Exception as e:
print(f"❌ Erro de conexão: {e}")
# 3. Informações sobre SSL
print(f"\n📋 Versão OpenSSL: {ssl.OPENSSL_VERSION}")
print(f"📋 Caminhos de verificação padrão: {ssl.get_default_verify_paths()}")
# Uso
diagnose_ssl_proxy(
'https://httpbin.org/ip',
'http://proxy:8080'
)
Wireshark para análise profunda
Para casos complexos, use Wireshark com filtro de tráfego TLS. Isso permite ver os detalhes do handshake e determinar exatamente o momento da falha.
# Filtros Wireshark para análise TLS
tls.handshake.type == 1 # Client Hello
tls.handshake.type == 2 # Server Hello
tls.handshake.type == 11 # Certificate
tls.alert_message # Alertas TLS (erros)
# Capturar tráfego com tcpdump
tcpdump -i eth0 -w ssl_debug.pcap \
'port 443 or port 8080' -c 1000
Melhores práticas de trabalho seguro
Seguir as melhores práticas ajudará a evitar a maioria dos problemas com SSL/TLS e garantirá a segurança dos seus dados ao trabalhar através de proxy.
Configuração de TLS
- Use no mínimo TLS 1.2, preferencialmente TLS 1.3
- Configure cipher suites modernos (ECDHE, AES-GCM, ChaCha20)
- Ative verificação de certificados (nunca desabilite verify em produção)
- Atualize regularmente o CA-bundle e bibliotecas criptográficas
Trabalho com proxy
- Prefira proxies com suporte completo ao método CONNECT para tráfego HTTPS
- Use SOCKS5 para tunelamento universal
- Ao trabalhar com proxies residenciais, considere possíveis características de redes de provedores
- Armazene credenciais de proxy em variáveis de ambiente, não no código
Tratamento de erros
import requests
from requests.exceptions import SSLError, ProxyError, ConnectionError
import time
def robust_request(url, proxy, max_retries=3):
"""Solicitação resistente a erros através de proxy"""
for attempt in range(max_retries):
try:
response = requests.get(
url,
proxies={'https': proxy},
timeout=30
)
return response
except SSLError as e:
print(f"Erro SSL (tentativa {attempt + 1}): {e}")
# Erros SSL geralmente não são corrigidos por retry
if 'CERTIFICATE_VERIFY_FAILED' in str(e):
raise # Requer correção de configuração
except ProxyError as e:
print(f"Erro de proxy (tentativa {attempt + 1}): {e}")
time.sleep(2 ** attempt) # Atraso exponencial
except ConnectionError as e:
print(f"Erro de conexão (tentativa {attempt + 1}): {e}")
time.sleep(2 ** attempt)
raise Exception(f"Falha ao executar solicitação após {max_retries} tentativas")
Checklist de resolução de problemas
Ao encontrar erros SSL/TLS, siga esta ordem de diagnóstico:
- Verifique a disponibilidade do servidor proxy (ping, telnet)
- Certifique-se de que o método CONNECT é suportado
- Verifique a correção das credenciais de autenticação
- Estude o certificado usando openssl s_client
- Verifique a presença de inspeção MITM
- Instale os certificados CA necessários
- Atualize a versão de TLS e cipher suites
💡 Dica: A maioria dos problemas de SSL/TLS ao trabalhar através de proxy está relacionada a certificados. Comece o diagnóstico verificando a cadeia de certificados e certifique-se de que todos os certificados intermediários estão presentes.
Conclusão
Problemas de SSL/TLS ao trabalhar através de proxy são solucionáveis com uma abordagem sistemática para diagnóstico. Os pontos-chave são: compreender a diferença entre tunelamento CONNECT e proxy MITM, configuração correta de certificados e uso de versões modernas do protocolo TLS. A maioria dos erros está relacionada à falta de certificados CA necessários ou incompatibilidade de parâmetros criptográficos.
Para tarefas de parsing e automação que requerem funcionamento estável de HTTPS, recomenda-se usar proxies de qualidade com suporte completo ao método CONNECT — mais detalhes sobre opções em proxycove.com.