2011-07-06 37 views
13

Tôi đang viết một proxy HTTP và tôi không hiểu một số chi tiết về việc thực hiện một yêu cầu CONNECT trên TLS. Để có được bức tranh tốt hơn, tôi đang thử nghiệm với Apache để quan sát cách nó tương tác với khách hàng. Đây là từ máy chủ ảo mặc định của tôi.CONNECT yêu cầu proxy HTTP chuyển tiếp qua kết nối 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> 

Cuộc trò chuyện giữa Apache và khách hàng của tôi diễn ra như thế này.

a. khách hàng kết nối với example.com:443 và gửi example.com trong quá trình bắt tay TLS.

b. máy khách gửi yêu cầu HTTP.

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

c. Apache nói HTTP/1.1 400 Bad Request. Nhật ký lỗi Apache cho biết

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

Dường như Apache không nhìn vào tiêu đề Máy chủ khác hơn là thấy nó ở đó vì HTTP/1.1 yêu cầu nó. Tôi nhận được hành vi không giống hệt nhau nếu khách hàng gửi Host: foo. Nếu tôi thực hiện yêu cầu HTTP đến example.com:80 mà không có TLS, thì Apache sẽ kết nối tôi với 192.168.1.1:22.

Tôi không hoàn toàn hiểu hành vi này. Có điều gì sai với yêu cầu CONNECT không? Tôi dường như không thể định vị các phần liên quan của RFC giải thích tất cả điều này.

+1

SNI ở trên có nghĩa là tên máy chủ được gửi trong phần bắt tay chứ không phải tiêu đề máy chủ lưu trữ. Như được viết trong câu trả lời của tôi dưới đây trộn SSL và CONNECT proxy không phải là điển hình. Có vẻ như Apache không mong đợi điều này chút nào vì nó xác thực chứng chỉ. Bạn có thể thử 'SSLStrictSNIVHostCheck off' trong Apache. – eckes

Trả lời

31

Không rõ liệu bạn đang cố gắng sử dụng Apache Httpd làm máy chủ proxy, điều này sẽ giải thích mã trạng thái 400 bạn đang nhận được. CONNECT được khách hàng sử dụng và được gửi đến máy chủ proxy (có thể là Apache Httpd, nhưng thường không), không phải máy chủ web đích.

CONNECT được sử dụng giữa máy khách và máy chủ proxy trước khi thiết lập kết nối TLS giữa máy khách và máy chủ kết thúc. Các khách hàng (C) kết nối đến proxy (P) proxy.example.com và gửi yêu cầu này (bao gồm cả dòng trống):

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

Proxy mở một kết nối TCP để www.example.com:443 (PS) và đáp ứng cho khách hàng với một trạng thái 200 mã, chấp nhận yêu cầu:

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

Sau đó, kết nối giữa máy khách và proxy (CP) được giữ mở. Máy chủ proxy chuyển tiếp mọi thứ trên kết nối C-P đến và đi từ P-S. Máy khách nâng cấp kết nối hoạt động (P-S) của nó lên kết nối SSL/TLS, bằng cách bắt đầu một cái bắt tay TLS trên kênh đó. Vì mọi thứ bây giờ được chuyển tiếp đến máy chủ, nó giống như trao đổi TLS được thực hiện trực tiếp với www.example.com:443.

Proxy không đóng bất kỳ vai trò nào trong quá trình bắt tay (và do đó với SNI). Việc bắt tay TLS có hiệu quả xảy ra trực tiếp giữa máy khách và máy chủ kết thúc.

Nếu bạn đang viết máy chủ proxy, tất cả những gì bạn cần làm để cho phép máy khách kết nối với máy chủ HTTPS được đọc trong yêu cầu CONNECT, tạo kết nối từ proxy đến máy chủ kết thúc (được đưa ra trong yêu cầu CONNECT), gửi cho khách hàng câu trả lời 200 OK và sau đó chuyển tiếp mọi thứ mà bạn đã đọc từ máy khách đến máy chủ và ngược lại.

RFC 2616 xử lý CONNECT như một cách để thiết lập đường hầm đơn giản (có nghĩa là). Có nhiều thông tin về nó trong RFC 2817, mặc dù phần còn lại của RFC 2817 (nâng cấp lên TLS trong một kết nối HTTP không ủy quyền) hiếm khi được sử dụng.

Có vẻ như những gì bạn đang cố gắng làm là có kết nối giữa máy khách (C) và proxy (P) trên TLS. Đó là tốt, nhưng khách hàng sẽ không sử dụng CONNECT để kết nối với máy chủ web bên ngoài (trừ khi đó là một kết nối đến một máy chủ HTTPS quá).

+0

1) Muốn hiểu, tại sao một khách hàng lại sử dụng HTTP "CONNECT", khi nó có thể trực tiếp sử dụng SSL để nói chuyện với máy chủ kết thúc? Cho dù đó là "CONNECT" hoặc SSL anyways nó sẽ đi qua các proxy được cấu hình. 2) Ngoài ra, trong trường hợp tiêu đề được gửi, khách hàng chỉ định địa chỉ máy chủ proxy trung gian trong yêu cầu "CONNECT"? – Sandeep

3

Từ RFC 2616 (Phần 14,23):

The Host trường yêu cầu-header xác định các máy chủ Internet và cổng số của tài nguyên được yêu cầu, như thu được từ bản gốc URI do người dùng cung hoặc tài nguyên giới thiệu (thường là URL HTTP, như được mô tả trong phần 3.2.2). Giá trị trường máy chủ PHẢI đại diện cho quyền đặt tên của máy chủ gốc hoặc cổng được cung cấp bởi URL gốc .

Hiểu biết của tôi là bạn cần phải sao chép địa chỉ từ đường CONNECT sang dòng HOST. Tất cả trong tất cả, địa chỉ của tài nguyên là 192.168.1.1 và thực tế là bạn đang kết nối qua example.com không thay đổi bất cứ điều gì từ quan điểm RFC.

+0

Theo mục 5.2, "2. Nếu URI yêu cầu không phải là absoluteURI và yêu cầu bao gồm trường tiêu đề máy chủ, máy chủ được xác định bởi giá trị trường tiêu đề máy chủ". Đối với CONNECT, URI yêu cầu không phải là absoluteURI (phần 5.1.2). – sigjuice

+0

@sigjuice ... Vì vậy, 5.2 chỉ không áp dụng (và tại sao bạn lại gọi nó?) –

+0

Từ 5.1.2, "Yêu cầu-URI =" * "| absoluteURI | abs_path | authority". CONNECT sử dụng biểu mẫu quyền của URI yêu cầu. Sau đó, từ 5.2 "Tài nguyên chính xác được xác định bởi yêu cầu Internet được xác định bằng cách kiểm tra cả trường Yêu cầu-URI và trường Tiêu đề máy chủ." IHMO, Apache nên sử dụng tiêu đề Máy chủ để xác định máy chủ và không bị lỗi với lỗi "máy chủ được cung cấp bởi SNI và máy chủ được cung cấp bởi HTTP khác nhau (example.com vs 192.168.1.1). – sigjuice

2

Rất hiếm khi thấy Phương thức CONNECT bên trong TLS (https). Tôi thực sự không biết bất kỳ khách hàng nào làm điều đó (và tôi sẽ quan tâm để biết ai đó làm, vì tôi nghĩ rằng nó thực sự là một tính năng tốt).

Thông thường máy khách kết nối với http (đồng bằng tcp) tới proxy và gửi phương thức CONNECT (và tiêu đề máy chủ) cho máy chủ: 443. Sau đó, proxy sẽ thực hiện một kết nối trong suốt tới điểm cuối và sau đó máy khách gửi bắt tay SSL thông qua.

Trong trường hợp này, dữ liệu được bảo vệ bằng ssl "từ đầu đến cuối".

Phương thức CONNECT không thực sự được chỉ định, nó chỉ được đặt trước trong RFC HTTP. Nhưng thông thường nó khá đơn giản nên nó tương thích. Phương thức chỉ định host [: port]. Host: tiêu đề có thể được bỏ qua. Một số tiêu đề xác thực proxy bổ sung có thể cần thiết. Khi phần thân của kết nối bắt đầu, việc phân tích cú pháp phải xảy ra bởi proxy nữa (một số làm, bởi vì chúng kiểm tra bắt tay SSL hợp lệ).

+1

Nó được xác định trong [RFC2817] (http://tools.ietf.org/html/rfc2817#section-5.2). – schlamar

+0

Cảm ơn @schlamar, gợi ý tốt. – eckes

+0

BTW: Chrome hỗ trợ kết nối SSL tới proxy: http://www.chromium.org/developers/design-documents/secure-web-proxy – eckes

3

Bạn đang làm mọi thứ đúng. Đó là Apache có điều sai trái. Hỗ trợ cho CONNECT qua TLS chỉ được thêm gần đây (https://issues.apache.org/bugzilla/show_bug.cgi?id=29744) và vẫn còn một số điều cần được giải quyết. Vấn đề bạn đang gặp là một trong số đó.

Các vấn đề liên quan