2011-07-06 9 views
13

Sto scrivendo un proxy HTTP e non riesco a capire alcuni dettagli sulla creazione di una richiesta CONNECT su TLS. Per ottenere un'immagine migliore, sto sperimentando Apache per osservare come interagisce con i clienti. Questo è dal mio host virtuale predefinito.CONNECT request to a forward proxy HTTP su una connessione SSL?

NameVirtualHost *:443 
<VirtualHost> 
    ServerName example.com 
    DocumentRoot htdocs/example.com 
    ProxyRequests On 
    AllowConnect 22 
    SSLEngine on 
    SSLCertificateFile /root/ssl/example.com-startssl.pem 
    SSLCertificateKeyFile /root/ssl/example.com-startssl.key 
    SSLCertificateChainFile /root/ssl/sub.class1.server.ca.pem 
    SSLStrictSNIVHostCheck off 
</VirtualHost> 

La conversazione tra Apache e il mio client va così.

a. il client si connette a example.com:443 e invia example.com nell'handshake TLS.

b. il client invia una richiesta HTTP.

CONNECT 192.168.1.1:22 HTTP/1.1 
Host: example.com 
Proxy-Connection: Keep-Alive 

c. Apache dice HTTP/1.1 400 Bad Request. Il log degli errori di Apache dice

Hostname example.com provided via SNI and hostname 192.168.1.1 
provided via HTTP are different. 

Sembra che Apache non guarda l'intestazione host diverso da quello di vedere che è lì da HTTP/1.1 richiede. Ottengo identico comportamento fallito se il client invia Host: foo. Se faccio la richiesta HTTP a example.com:80 senza TLS, allora Apache mi collegherà a 192.168.1.1:22.

Non capisco completamente questo comportamento. C'è qualcosa di sbagliato nella richiesta CONNECT? Non riesco a localizzare le parti rilevanti delle RFC che spiegano tutto questo.

+1

SNI sopra indica il nome host inviato nell'handshake, non l'intestazione host. Come scritto nella mia risposta qui sotto, la combinazione di proxy SSL e CONNECT non è tipica. Sembra che Apache non si aspetti affatto questo dato che convalida i certificati. Puoi provare 'SSLStrictSNIVHostCheck off 'in Apache. – eckes

risposta

31

Non è chiaro se si stia tentando di utilizzare Apache Httpd come server proxy, questo spiegherebbe il codice di stato 400 che si sta ottenendo. CONNECT viene utilizzato dal client e inviato al server proxy (probabilmente Apache Httpd, ma in genere non), non al server Web di destinazione.

CONNECT viene utilizzato tra il client e il server proxy prima di stabilire la connessione TLS tra il client e il server finale. Il client (C) si collega al proxy (P) proxy.example.com e invia questa richiesta (compreso riga vuota):

C->P: CONNECT www.example.com:443 HTTP/1.1 
C->P: Host: www.example.com:443 
C->P: 

Il proxy apre una connessione TCP www.example.com:443 (PS) e risponde al client con uno stato 200 codice, accettando la richiesta:

P->C: 200 OK 
P->C: 

Dopo questo, la connessione tra il client e il proxy (CP) viene mantenuto aperto. Il server proxy trasmette tutto sulla connessione C-P da e verso P-S. Il client aggiorna la sua connessione attiva (P-S) a una connessione SSL/TLS, avviando un handshake TLS su quel canale. Poiché tutto ora viene inoltrato al server, è come se lo scambio TLS fosse stato eseguito direttamente con www.example.com:443.

Il proxy non svolge alcun ruolo nell'handshake (e quindi con SNI). L'handshake TLS avviene in modo efficace direttamente tra il client e il server finale.

Se state scrivendo un server proxy, tutto quello che dovete fare per permettere ai vostri clienti di connettersi al server HTTPS viene letto nella richiesta CONNECT, effettuare una connessione dal proxy al server finale (data nella richiesta CONNECT), inviare al client una risposta 200 OK e quindi inoltrare tutto ciò che si legge dal client al server e viceversa.

RFC 2616 tratta come un modo per stabilire un tunnel semplice (che è). C'è altro su di esso in RFC 2817, anche se il resto di RFC 2817 (aggiornamenti a TLS all'interno di una connessione HTTP non proxy) viene usato raramente.

Sembra che quello che stai cercando di fare è avere la connessione tra il client (C) e il proxy (P) su TLS. Va bene, ma il client non utilizzerà CONNECT per connettersi a server Web esterni (a meno che non si tratti di una connessione a un server HTTPS).

+0

1) Volevo capire, perché un client dovrebbe mai usare "CONNECT" HTTP, quando può usare direttamente SSL per parlare con il server finale? Che si tratti di "CONNECT" o SSL, comunque, attraverserebbe i proxy configurati. 2) Anche in quale intestazione è archiviato il client specifica l'indirizzo del server proxy intermedio nella richiesta "CONNECT"? – Sandeep

3

Da RFC 2616 (sezione 14.23):

Il campo request-header Host specifica l'host di Internet e la porta stato richiesto il numero della risorsa, come ottenuto dall'originale URI dato dall'utente o risorsa di riferimento (generalmente un URL HTTP, come descritto nella sezione 3.2.2). Il valore del campo Host DEVE rappresentare l'autorità di denominazione del server di origine o del gateway fornita dall'URL originale .

La mia comprensione è che è necessario copiare l'indirizzo dalla linea CONNECT alla linea HOST. Tutto sommato, l'indirizzo della risorsa è 192.168.1.1, e il fatto che ci si connetta tramite esempio.com non cambia nulla dal punto di vista RFC.

+0

In base alla sezione 5.2, "2. Se l'URI di richiesta non è un assolutoURI e la richiesta include un campo di intestazione Host, l'host viene determinato dal valore del campo dell'intestazione Host." Per CONNECT, Request-URI non è un absoluteURI (sezione 5.1.2). – sigjuice

+0

@sigjuice ... Quindi 5.2 non si applica (e perché vi siete riferiti?) –

+0

Da 5.1.2, "Request-URI =" * "| absoluteURI | abs_path | authority". CONNECT utilizza il modulo di autorizzazione dell'Universo di richiesta. Quindi, da 5.2 "La risorsa esatta identificata da una richiesta Internet è determinata esaminando sia l'URI della richiesta sia il campo dell'intestazione Host." IHMO, Apache dovrebbe utilizzare l'intestazione Host per determinare l'host e non fallire con l'errore "host fornito da SNI e l'host fornito da HTTP sono diversi (example.com vs 192.168.1.1). – sigjuice

2

È abbastanza raro vedere il metodo CONNECT in TLS (https). In realtà non conosco nessun cliente che lo faccia (e sarei interessato a sapere chi lo fa, perché penso che sia in realtà una buona caratteristica).

Normalmente il client si collega con http (plain tcp) al proxy e invia il metodo CONNECT (e l'intestazione host) all'host: 443. Quindi il proxy eseguirà una connessione trasparente con l'endpoint e quindi il client invierà l'handshake SSL.

In questo scenario i dati sono protetti "end-to-end".

Il metodo CONNECT non è specificato, è riservato solo nell'RFC HTTP. Ma in genere è abbastanza semplice, quindi è interoperabile. Il metodo specifica l'host [: port]. Host: l'intestazione può essere semplicemente ignorata. Potrebbero essere necessarie alcune intestazioni di autenticazione proxy aggiuntive. Quando inizia il corpo della connessione, il proxy non deve più eseguire l'analisi (alcuni lo fanno perché controllano l'handshake SSL valido).

+1

È specificato in [RFC2817] (http://tools.ietf.org/html/rfc2817#section-5.2). – schlamar

+0

Grazie a @schlamar, buon suggerimento. – eckes

+0

BTW: Chrome supporta connessioni SSL ai proxy: http://www.chromium.org/developers/design-documents/secure-web-proxy – eckes