Torna al blog

Risoluzione dei Problemi SSL/TLS con Proxy: Guida Completa

Analizziamo gli errori comuni di SSL/TLS quando si lavora attraverso proxy: dai problemi di certificati alla configurazione dei tunnel HTTPS. Soluzioni pratiche con esempi di codice.

📅16 dicembre 2025
```html

Risoluzione dei problemi SSL/TLS quando si utilizza un proxy: guida completa

Gli errori SSL/TLS sono uno dei problemi più frequenti quando si lavora attraverso server proxy. Una configurazione errata porta a guasti della connessione, perdite di dati e impossibilità di analizzare risorse protette. In questa guida analizzeremo le cause degli errori e i modi per eliminarli con esempi di codice specifici.

Come funziona SSL/TLS attraverso un proxy

Prima di risolvere i problemi, è importante comprendere il meccanismo di funzionamento delle connessioni protette attraverso un proxy. Esistono due approcci fondamentalmente diversi al proxying del traffico HTTPS, e ognuno ha le sue caratteristiche, vantaggi e potenziali punti di guasto.

Tunneling trasparente (CONNECT)

Quando si utilizza il metodo CONNECT, il server proxy crea un tunnel TCP tra il client e il server di destinazione. Il proxy non vede il contenuto del traffico: trasmette semplicemente i byte crittografati in entrambe le direzioni. Questo è il metodo più sicuro e diffuso per lavorare con HTTPS attraverso un proxy.

Il processo di stabilimento della connessione è il seguente: il client invia una richiesta CONNECT al proxy specificando l'host e la porta di destinazione. Il proxy stabilisce una connessione TCP con il server di destinazione e restituisce al client una risposta 200 Connection Established. Successivamente, il client esegue l'handshake TLS direttamente con il server di destinazione attraverso il tunnel stabilito.

# Schema del tunnel CONNECT
Client → Proxy: CONNECT example.com:443 HTTP/1.1
Proxy → Server: [Connessione TCP sulla porta 443]
Proxy → Client: HTTP/1.1 200 Connection Established
Client ↔ Server: [Handshake TLS attraverso il tunnel]
Client ↔ Server: [Traffico crittografato]

Proxying MITM (intercettazione)

Alcuni proxy (in particolare quelli aziendali) utilizzano la tecnica Man-in-the-Middle: terminano la connessione TLS dal loro lato, decrittografano il traffico e quindi stabiliscono una nuova connessione TLS con il server di destinazione. A tal fine, il proxy genera il proprio certificato per ogni dominio, firmato dal certificato radice del proxy.

Questo approccio consente di ispezionare e filtrare il traffico HTTPS, ma richiede l'installazione del certificato radice del proxy nell'archivio attendibile del client. È qui che sorgono la maggior parte dei problemi SSL/TLS quando si lavora attraverso reti aziendali.

Errori tipici e loro cause

Consideriamo gli errori SSL/TLS più comuni quando si lavora attraverso un proxy. Comprendere la causa di ogni errore è la chiave per risolvere rapidamente il problema.

SSL: CERTIFICATE_VERIFY_FAILED

Questo errore significa che il client non può verificare l'autenticità del certificato del server. Le cause possono essere diverse: il proxy utilizza l'ispezione MITM con un certificato radice non installato, il certificato del server di destinazione è scaduto o autofirmato, oppure manca un certificato intermedio nella catena.

# Python - messaggio di errore tipico
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

L'errore si verifica quando il client tenta di stabilire una connessione TLS ma riceve una risposta in un formato inaspettato. Molto spesso ciò accade quando il proxy non supporta il metodo CONNECT e tenta di elaborare la richiesta HTTPS come HTTP ordinario. Anche una porta o un protocollo errato potrebbe essere la causa.

TLSV1_ALERT_PROTOCOL_VERSION

Il server rifiuta la connessione a causa di una versione del protocollo TLS incompatibile. I server moderni disabilitano le versioni obsolete di TLS 1.0 e TLS 1.1 per motivi di sicurezza. Se il proxy o il client sono configurati per utilizzare versioni precedenti, la connessione verrà rifiutata.

SSLV3_ALERT_HANDSHAKE_FAILURE

Il client e il server non hanno potuto concordare i parametri di crittografia. Ciò può accadere a causa dell'assenza di suite di cifratura comuni, problemi con SNI (Server Name Indication) o incompatibilità delle librerie crittografiche.

Errore Causa probabile Soluzione
CERTIFICATE_VERIFY_FAILED Proxy MITM, certificato scaduto Installare il certificato radice del proxy
WRONG_VERSION_NUMBER HTTP invece di HTTPS, nessun supporto CONNECT Verificare le impostazioni del proxy e il protocollo
PROTOCOL_VERSION Versione TLS obsoleta Aggiornare la versione TLS minima
HANDSHAKE_FAILURE Suite di cifratura incompatibili Configurare l'elenco dei cifrari

Problemi con i certificati

I certificati sono la fonte più frequente di problemi quando si lavora attraverso un proxy. Consideriamo gli scenari principali e i modi per risolverli.

Installazione del certificato radice del proxy

Se il proxy utilizza l'ispezione MITM, è necessario aggiungere il suo certificato radice all'archivio attendibile. Il processo dipende dal sistema operativo e dal linguaggio di programmazione utilizzato.

# Linux: aggiunta del certificato all'archivio di sistema
sudo cp proxy-ca.crt /usr/local/share/ca-certificates/
sudo update-ca-certificates

# macOS: aggiunta a Keychain
sudo security add-trusted-cert -d -r trustRoot \
    -k /Library/Keychains/System.keychain proxy-ca.crt

# Windows PowerShell (da amministratore)
Import-Certificate -FilePath "proxy-ca.crt" `
    -CertStoreLocation Cert:\LocalMachine\Root

Specifica del certificato nel codice

A volte è più conveniente specificare il percorso del certificato direttamente nel codice, soprattutto quando si lavora in container o su server senza accesso root. Ciò consente di evitare la modifica delle impostazioni di sistema.

# Python: specifica del proprio CA-bundle
import requests

# Metodo 1: specificare il percorso del file certificato
response = requests.get(
    'https://example.com',
    proxies={'https': 'http://proxy:8080'},
    verify='/path/to/proxy-ca.crt'
)

# Metodo 2: combinare con i certificati di sistema
import certifi
import ssl

# Creare un file di certificati combinato
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')

Disabilitazione della verifica dei certificati

In alcuni casi (solo per il test e il debug!) potrebbe essere necessario disabilitare la verifica dei certificati. Questo non è sicuro e non deve essere utilizzato in produzione, poiché rende la connessione vulnerabile agli attacchi MITM.

⚠️ Avvertenza: La disabilitazione della verifica dei certificati rende la connessione vulnerabile all'intercettazione. Utilizzare solo per il debug in un ambiente isolato, mai in produzione!

# Python - SOLO per il debug!
import requests
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

response = requests.get(
    'https://example.com',
    proxies={'https': 'http://proxy:8080'},
    verify=False  # Non sicuro!
)

Configurazione dei tunnel CONNECT

La corretta configurazione del tunnel CONNECT è la chiave per un lavoro stabile con HTTPS attraverso un proxy. Consideriamo le caratteristiche della configurazione per diversi scenari di utilizzo.

Verifica del supporto CONNECT

Non tutti i proxy supportano il metodo CONNECT. I proxy HTTP possono essere configurati solo per il proxying del traffico HTTP. Prima dell'utilizzo, assicurarsi che il proxy supporti il tunneling HTTPS.

# Verifica del supporto CONNECT tramite 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

# Risposta prevista in caso di successo:
# HTTP/1.1 200 Connection Established

# Possibili risposte in caso di errore:
# HTTP/1.1 403 Forbidden - CONNECT è vietato
# HTTP/1.1 405 Method Not Allowed - metodo non supportato

Autenticazione nelle richieste CONNECT

Quando si utilizza un proxy con autenticazione, è importante trasmettere correttamente le credenziali. L'intestazione Proxy-Authorization deve essere presente nella richiesta CONNECT.

# Python: autenticazione tramite URL
import requests

proxy_url = 'http://username:password@proxy.example.com:8080'
response = requests.get(
    'https://target.com',
    proxies={'https': proxy_url}
)

# Python: autenticazione tramite intestazioni (per casi complessi)
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')

Lavoro con proxy SOCKS5

I proxy SOCKS5 funzionano a un livello inferiore e non richiedono un trattamento speciale di HTTPS. Semplicemente tunnelizzano le connessioni TCP, il che li rende ideali per lavorare con qualsiasi protocollo.

# Python con PySocks
import requests

proxies = {
    'http': 'socks5h://user:pass@proxy:1080',
    'https': 'socks5h://user:pass@proxy:1080'
}

# socks5h - risoluzione DNS attraverso il proxy
# socks5 - risoluzione DNS locale

response = requests.get('https://example.com', proxies=proxies)

Soluzioni per diversi linguaggi di programmazione

Ogni linguaggio e libreria hanno le loro caratteristiche nel lavoro con SSL/TLS attraverso un proxy. Consideriamo le varianti più comuni con esempi di codice completi.

Python (requests + urllib3)

import requests
from requests.adapters import HTTPAdapter
from urllib3.util.ssl_ import create_urllib3_context
import ssl

class TLSAdapter(HTTPAdapter):
    """Adattatore con parametri TLS personalizzabili"""
    
    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)

# Creazione di un contesto con impostazioni moderne
ctx = create_urllib3_context()
ctx.minimum_version = ssl.TLSVersion.TLSv1_2
ctx.set_ciphers('ECDHE+AESGCM:DHE+AESGCM:ECDHE+CHACHA20')

# Caricamento di un certificato aggiuntivo
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');

// Caricamento del certificato CA aggiuntivo
const customCA = fs.readFileSync('proxy-ca.crt');

const agent = new HttpsProxyAgent({
    host: 'proxy.example.com',
    port: 8080,
    auth: 'username:password',
    ca: customCA,
    rejectUnauthorized: true  // Non disabilitare in produzione!
});

const client = axios.create({
    httpsAgent: agent,
    proxy: false  // Importante: disabilitare il proxy integrato di 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 con il certificato:', error.message);
        }
        throw error;
    }
}

fetchData();

Go (net/http)

package main

import (
    "crypto/tls"
    "crypto/x509"
    "io/ioutil"
    "net/http"
    "net/url"
    "log"
)

func main() {
    // Caricamento del certificato aggiuntivo
    caCert, err := ioutil.ReadFile("proxy-ca.crt")
    if err != nil {
        log.Fatal(err)
    }
    
    caCertPool := x509.NewCertPool()
    caCertPool.AppendCertsFromPEM(caCert)
    
    // Configurazione TLS
    tlsConfig := &tls.Config{
        RootCAs:    caCertPool,
        MinVersion: tls.VersionTLS12,
    }
    
    // Configurazione del 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 (riga di comando)

# Richiesta di base attraverso il proxy
curl -x http://proxy:8080 https://example.com

# Con autenticazione
curl -x http://proxy:8080 -U username:password https://example.com

# Con specifica del certificato CA
curl -x http://proxy:8080 --cacert proxy-ca.crt https://example.com

# Forzare l'uso di TLS 1.2+
curl -x http://proxy:8080 --tlsv1.2 https://example.com

# Debug dell'handshake TLS
curl -x http://proxy:8080 -v --trace-ascii - https://example.com 2>&1 | \
    grep -E "(SSL|TLS|certificate)"

Rilevamento e bypass dell'ispezione MITM

Alcune reti (aziendali, Wi-Fi pubblico) utilizzano l'ispezione MITM trasparente del traffico HTTPS. Ciò può causare problemi con il certificate pinning e interrompere il funzionamento delle applicazioni che si aspettano certificati specifici.

Rilevamento del proxy MITM

import ssl
import socket

def check_certificate_issuer(hostname, port=443):
    """Verifica chi ha emesso il certificato del sito"""
    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')}")
            
            # Proxy MITM aziendali noti
            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 rilevato: {indicator}")
                    return True
            
            return False

# Verifica
check_certificate_issuer('google.com')

Lavoro in condizioni MITM

Se viene rilevato un proxy MITM, ci sono diverse strategie di lavoro. È possibile installare il certificato radice del proxy (se si tratta di una rete aziendale attendibile), utilizzare porte o protocolli alternativi, oppure applicare una VPN per aggirare l'ispezione.

# Ottenimento del certificato del proxy MITM per l'installazione
openssl s_client -connect example.com:443 -proxy proxy:8080 \
    -showcerts 2>/dev/null | \
    openssl x509 -outform PEM > mitm-cert.pem

# Visualizzazione delle informazioni sul certificato
openssl x509 -in mitm-cert.pem -text -noout | head -20

Strumenti di diagnostica

La corretta diagnostica è metà della soluzione del problema. Consideriamo gli strumenti e i metodi per il debug delle connessioni SSL/TLS attraverso un proxy.

OpenSSL per la diagnostica

# Verifica della connessione attraverso il proxy
openssl s_client -connect example.com:443 \
    -proxy proxy.example.com:8080 \
    -servername example.com

# Verifica della catena di certificati
openssl s_client -connect example.com:443 \
    -proxy proxy:8080 \
    -showcerts 2>/dev/null | \
    grep -E "(Certificate chain|s:|i:)"

# Test di una versione TLS specifica
openssl s_client -connect example.com:443 \
    -proxy proxy:8080 \
    -tls1_2

# Verifica dei cipher suites supportati
openssl s_client -connect example.com:443 \
    -proxy proxy:8080 \
    -cipher 'ECDHE-RSA-AES256-GCM-SHA384'

Script Python per la diagnostica

import ssl
import socket
import requests
from urllib.parse import urlparse

def diagnose_ssl_proxy(target_url, proxy_url):
    """Diagnostica completa di SSL attraverso il proxy"""
    
    print(f"🔍 Diagnostica: {target_url}")
    print(f"📡 Proxy: {proxy_url}\n")
    
    # 1. Verifica della disponibilità del proxy
    proxy = urlparse(proxy_url)
    try:
        sock = socket.create_connection(
            (proxy.hostname, proxy.port), 
            timeout=10
        )
        sock.close()
        print("✅ Proxy disponibile")
    except Exception as e:
        print(f"❌ Proxy non disponibile: {e}")
        return
    
    # 2. Verifica del metodo CONNECT
    try:
        response = requests.get(
            target_url,
            proxies={'https': proxy_url},
            timeout=15
        )
        print(f"✅ Connessione HTTPS riuscita: {response.status_code}")
    except requests.exceptions.SSLError as e:
        print(f"❌ Errore SSL: {e}")
        
        # Tentativo senza verifica per la diagnostica
        try:
            response = requests.get(
                target_url,
                proxies={'https': proxy_url},
                verify=False,
                timeout=15
            )
            print("⚠️ La connessione funziona senza verifica del certificato")
            print("   Probabilmente problema con il certificato CA del proxy")
        except Exception as e2:
            print(f"❌ Non funziona nemmeno senza verifica: {e2}")
    
    except Exception as e:
        print(f"❌ Errore di connessione: {e}")
    
    # 3. Informazioni su SSL
    print(f"\n📋 Versione OpenSSL: {ssl.OPENSSL_VERSION}")
    print(f"📋 Percorsi di verifica predefiniti: {ssl.get_default_verify_paths()}")

# Utilizzo
diagnose_ssl_proxy(
    'https://httpbin.org/ip',
    'http://proxy:8080'
)

Wireshark per l'analisi approfondita

Per i casi complessi, utilizzare Wireshark con un filtro sul traffico TLS. Ciò consente di vedere i dettagli dell'handshake e determinare esattamente il momento del guasto.

# Filtri Wireshark per l'analisi TLS
tls.handshake.type == 1          # Client Hello
tls.handshake.type == 2          # Server Hello
tls.handshake.type == 11         # Certificate
tls.alert_message                 # Avvisi TLS (errori)

# Cattura del traffico tramite tcpdump
tcpdump -i eth0 -w ssl_debug.pcap \
    'port 443 or port 8080' -c 1000

Migliori pratiche per il lavoro sicuro

Il rispetto delle migliori pratiche aiuterà a evitare la maggior parte dei problemi SSL/TLS e garantirà la sicurezza dei dati quando si lavora attraverso un proxy.

Configurazione TLS

  • Utilizzare almeno TLS 1.2, preferibilmente TLS 1.3
  • Configurare suite di cifratura moderne (ECDHE, AES-GCM, ChaCha20)
  • Abilitare la verifica dei certificati (non disabilitare mai verify in produzione)
  • Aggiornare regolarmente il CA-bundle e le librerie crittografiche

Lavoro con il proxy

  • Preferire proxy con supporto completo del metodo CONNECT per il traffico HTTPS
  • Utilizzare SOCKS5 per il tunneling universale
  • Quando si lavora con proxy residenziali tenere conto delle possibili caratteristiche delle reti dei provider
  • Memorizzare le credenziali del proxy nelle variabili di ambiente, non nel codice

Gestione degli errori

import requests
from requests.exceptions import SSLError, ProxyError, ConnectionError
import time

def robust_request(url, proxy, max_retries=3):
    """Richiesta resistente agli errori attraverso il 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"Errore SSL (tentativo {attempt + 1}): {e}")
            # Gli errori SSL di solito non si risolvono con i tentativi
            if 'CERTIFICATE_VERIFY_FAILED' in str(e):
                raise  # Richiede correzione della configurazione
                
        except ProxyError as e:
            print(f"Errore proxy (tentativo {attempt + 1}): {e}")
            time.sleep(2 ** attempt)  # Ritardo esponenziale
            
        except ConnectionError as e:
            print(f"Errore di connessione (tentativo {attempt + 1}): {e}")
            time.sleep(2 ** attempt)
    
    raise Exception(f"Impossibile eseguire la richiesta dopo {max_retries} tentativi")

Checklist per la risoluzione dei problemi

Quando si verificano errori SSL/TLS, seguire questo ordine di diagnostica:

  1. Verificare la disponibilità del server proxy (ping, telnet)
  2. Assicurarsi del supporto del metodo CONNECT
  3. Verificare la correttezza delle credenziali di autenticazione
  4. Esaminare il certificato tramite openssl s_client
  5. Verificare la presenza dell'ispezione MITM
  6. Installare i certificati CA necessari
  7. Aggiornare la versione TLS e le suite di cifratura

💡 Suggerimento: La maggior parte dei problemi SSL/TLS quando si lavora attraverso un proxy sono correlati ai certificati. Iniziare la diagnostica verificando la catena di certificati e assicurarsi che tutti i certificati intermedi siano presenti.

Conclusione

I problemi SSL/TLS quando si lavora attraverso un proxy sono risolvibili con un approccio sistematico alla diagnostica. I punti chiave sono: comprendere la differenza tra tunneling CONNECT e proxying MITM, configurare correttamente i certificati e utilizzare versioni moderne del protocollo TLS. La maggior parte degli errori è correlata all'assenza dei certificati CA necessari o all'incompatibilità dei parametri crittografici.

Per attività di parsing e automazione che richiedono un lavoro stabile con HTTPS, si consiglia di utilizzare proxy di qualità con supporto completo del metodo CONNECT - maggiori dettagli sulle opzioni disponibili su proxycove.com.

```