Http/https代理和抓包分析

前言

最近工作需要部署http/https的代理,所以用squid部署了一下,重新回顾了一下http和https的代理知识。

HTTP代理

根据《HTTP 权威指南》如图:

这种情况下,对访问服务器而言,它会把代理当做客户端,完全察觉不到真正客户端的存在,完全隐藏了客户端的IP,当然代理也可以修改Http头部,X-Forwarded-IP头部告诉服务器真正的客户端IP。

代理可以用两种方式,一种是代码实现,一种用现成的代理软件如squid,本文是用squid,配置文件如下(这里不详细介绍squid的使用,有需要单独另开来讲squid)。然后启动squid起来

代码实现

var http = require('http');
var net = require('net');
var url = require('url');

function request(cReq, cRes) {
    var u = url.parse(cReq.url);

    var options = {
        hostname : u.hostname, 
        port     : u.port || 80,
        path     : u.path,       
        method     : cReq.method,
        headers     : cReq.headers
    };

    var pReq = http.request(options, function(pRes) {
        cRes.writeHead(pRes.statusCode, pRes.headers);
        pRes.pipe(cRes);
    }).on('error', function(e) {
        cRes.end();
    });

    cReq.pipe(pReq);
}

http.createServer().on('request', request).listen(3128, '0.0.0.0');

squid配置

# Squid normally listens to port 3128
http_port 3128

然后客户端使用curl命令通过代理访问百度(注意这里访问百度用的http协议而非https) 

curl -x http://43.129.198.37:3128 http://www.baidu.com

可以看到这时候访问成功了, 分析报文(报文分别是在客户端和服务端这边抓取的)由于是http,所以报文都是明文发送的,分别可以看到客户端和代理TCP的三次握手,代理和服务器TCP三次握手,以及http报文。

但是上述的过程能不能代理https的流量呢,答案是代码形式的不行,squid的可以,这就涉及到第二个内容http的隧道代理

HTTP隧道代理-代理https流量

也就是上述squid配置不变,把命令改成:

curl -x http://43.129.198.37:3128 https://www.baidu.com 把百度协议改成https它依然能够成功,因为squid是有http的隧道代理的。

还是抄《HTTP 权威指南》图,首先客户端会发送connect请求将需要访问域名发送给proxy,proxy会回应http connect established就绪报文,并且proxy和server建立了TCP通道,后续proxy会进行盲转发client和server之间报文

客户端到proxy之间报文:

 proxy和server之间的报文:

 

直接上报文讲解,可以 看到在客户端和proxy之间发送connect请求,然后proxy回了connection established,后续就是TLS的通信过程。proxy和server会创建一个TCP通道,然后后续client发送给server的TLS通信过程,proxy只是进行盲转发,如何验证这点?请看如图:

把TLS第一个Client hello报文拿出来分别对比,可以看到客户端到proxy,proxy到server的client hello的随机值Random都是一样的,证明proxy只是进行盲转发。

HTTPS代理

很多时候在讨论代理,因为发现很多人都混淆了https代理和代理https流量。如上面http隧道代理,它能支持客户端访问服务器,也就是说它能代理https流量,但它是http代理,因为站在客户端角度,它配置的就是http代理,如上述curl命令就是客户端:

-x http://43.129.198.37:3128,填的协议就是http

再比如switchyOmega配置也是配置http,因为本质上客户端和proxy之间是http connect明文交互的。

所以https代理本质上指的就是,客户端和proxy之间交互是加密的,也就是上述的http connect是加密的,客户端配置的代理协议是https。

squid配置文件修改:

https_port 443 cert=./myCA.pem

 那么curl命令就变为:

SSLKEYLOGFILE=./ssl-key.log curl -x https://43.129.198.37:443 https://www.baidu.com --proxy-cacert ./test2.pem

其中myCA.pem里面包含公私钥,curl命令的test.pem只包含公钥,这里加上了一个环境变量SSLKEYLOGFILE是用于wireshark进行解密的。

还是一样抓取client到proxy的流量,以及proxy到server的流量

 第一份报文是解密后的数据(绿色部分是解密后的报文。可以看到客户端和proxy之间先进行了TLSv1.3协商,协商完成后,加密了http connect数据,以及返回connection established(绿色部分前两个报文),如果不解密是只会看到TLS加密的数据 

 第二份报文是proxy和服务器的协商是TLSv1.2进行协商的,但是其实这时候proxy依然只是和server建立tcp连接,后续进行盲转发,本质上还是客户端和服务器的tls协商,只不过客户端和proxy先进行TLSv1.3协商,然后客户端再和服务器再进行TLS1.2协商,这中间TLSv1.3 加密了客户端与服务器的TLSv1.2协商过程。

要验证也很简单,就在客户端和proxy的TLSv1.3加密报文中找客户端和服务器的TLS1.2加密报文,来证明proxy只是进行tcp层的盲装发,就找第一个TLS1.2协商的client hello报文的随机数:

Squid-SSL-Bump模式

这种模式下,proxy是一种中间人的角色Man-in-middle,能够对客户端到服务器的加密信息进行解密。

squid的配置

http_port 3128 ssl-bump \
  cert=/etc/squid/ssl_cert/myCA.pem \
  generate-host-certificates=on dynamic_cert_mem_cache_size=4MB

# For squid 3.5.x
sslcrtd_program /usr/local/squid/libexec/ssl_crtd -s /var/lib/ssl_db -M 4MB

# For squid 4.x
# sslcrtd_program /usr/local/squid/libexec/security_file_certgen -s /var/lib/ssl_db -M 4MB

acl step1 at_step SslBump1

ssl_bump peek step1
ssl_bump bump all

启动后 

客户端用curl 命令试一下,可以看到访问百度时候,报私有证书错误,这是因为SSL-Bump模式proxy中间人角色,客户端<ssl/tls>proxy<ssl/tls>百度。

实际上是proxy访问百度,访问时百度会传递它的证书给proxy,proxy是信任百度证书的,所以这方面通信没问题。但同时百度传给proxy证书,proxy也会伪造一个类似证书返回给客户端,这个证书是基于proxy自己私有CA签名的,所以客户端收到是私有证书,因此会报私有证书错误。后面的curl如果加了CA证书,自然就可以通过了。

可以看到配置文件,还是http_port,所以这种方式还是HTTP 代理, 抓包分析:未完待续

参考文献:

Intercept HTTPS CONNECT messages with SSL-Bump | Squid Web Cache wiki (squid-cache.org)

猜你喜欢

转载自blog.csdn.net/u012895183/article/details/133848475