LOADING

加载过慢请开启缓存 浏览器默认开启

从报文层面分析HTTP正向代理和反向代理的区别

从报文层面分析正向代理和反向代理的区别

概念

首先先简单解释下正向代理和反向代理的概念区别

正向代理

正向代理代表客户端向外部服务器发起请求,主要用于绕过限制或提升访问效率。

链路:客户端->代理服务器->目标

反向代理

反向代理则接收客户端请求并分发给后台服务器处理,旨在优化服务性能和增强安全性。

链路:客户端->代理服务器->目标

可见,从链路上来说,正向代理与反向代理几乎是相同的,但是从功能上来说,正向代理可以代表客户端发往几乎任意的目标服务器,而反向代理却只能将请求发往预先配置的目标。

那么他们实际上到底有什么不同呢?

报文分析

直接请求

直接使用curl发送请求

curl http://www.baidu.com

使用tcpdump抓包并丢到wireshark中分析,可得

image.png

image.png

image.png

正向代理

首先使用tinyproxy搭建一个正向代理,这里使用第三方构建的docker镜像来快速部署

docker run -d -p 6666:8888 dannydirect/tinyproxy:latest ANY

然后通过正向代理向http://www.baidu.com发送请求

curl -x 'http://127.0.0.1:6666' http://www.baidu.com

使用tcpdump进行抓包

sudo tcpdump -i any port 6666 or host www.baidu.com -w output.pcap

抓到的pcap包丢到wireshark中进行分析

image.png

image.png

image.png

可见,虽然发送的目标地址已经是127.0.0.1:6666了,但是实际上的HTTP请求报文依旧与不使用代理的时候一样。唯一的不同是多了一个proxy-connection: keep-alive请求头。
该请求头的作用是告诉代理服务器,尝试维持与代理服务器之间的一个持久连接。

反向代理

使用nginx配置一个简单的代理服务器,端口8888,由于对于客户端来说,不知道反向代理后是什么,因此,此时的报文与直接请求是类似的,只是里面所有与目标baidu.com有关的全都为反向代理服务器了。

image.png

image.png

image.png

正向代理如何转发请求?

由上可知,虽然正向代理与反向代理的链路是相同的。
但报文上存在一些差异,IPV4以及TCP的报文都是大同小异的

区别就在于HTTP报文中的Host请求头以及首行的路径。

查看tinyproxy相关代码,定位到reqs.c中的handle_connection函数中的代码段。

connptr->server_fd = opensock (request->host, request->port,
                                               connptr->server_ip_addr);

可见,正向代理服务器是根据request->host, request->port建立与目标地址的连接的

request是使用如下代码行获取的

request = process_request (connptr, hashofheaders);

定位到process_request中的

ret = sscanf (connptr->request_line, "%[^ ] %[^ ] %[^ ]",
               request->method, url, request->protocol);

该行的作用是将connptr->request_line中的字符串进行解析,空格为分界符。

当正向代理时,connptr->request_line值为GET http://www.baidu.com/ HTTP/1.1,那么经过处理后,得到如下结果

request-method:GET
url:http://www.baidu.com/
request->protocol:HTTP/1.1

获得的url会经过extract_url函数处理

static int extract_url (const char *url, int default_port,
                        struct request_s *request)
{
        char *p;
        int port;

        /* Split the URL on the slash to separate host from path */
        p = strchr (url, '/');
        if (p != NULL) {
                int len;
                len = p - url;
                request->host = (char *) safemalloc (len + 1);
                memcpy (request->host, url, len);
                request->host[len] = '\0';
                request->path = safestrdup (p);
        } else {
                request->host = safestrdup (url);
                request->path = safestrdup ("/");
        }

        if (!request->host || !request->path)
                goto ERROR_EXIT;

        /* Remove the username/password if they're present */
        strip_username_password (request->host);

        /* Find a proper port in www.site.com:8001 URLs */
        port = strip_return_port (request->host);
        request->port = (port != 0) ? port : default_port;

        /* Remove any surrounding '[' and ']' from IPv6 literals */
        p = strrchr (request->host, ']');
        if (p && (*(request->host) == '[')) {
                memmove(request->host, request->host + 1,
                        strlen(request->host) - 2);
                *p = '\0';
                p--;
                *p = '\0';
        }

        return 0;

ERROR_EXIT:
        if (request->host)
                safefree (request->host);
        if (request->path)
                safefree (request->path);

        return -1;
}

最终得到

request->host:www.baidu.com
request->port:默认
request->path:/

因此,我们就能够最终确定目标服务器了,并且确定发送过去的报文。

结论

正向代理与反向代理虽然链路相同,但是由于HTTP报文的不同而带来截然不同的行为。
发送给正向代理的报文请求行中的Request-URI(统一资源标识符)带有全路径,以便与代理服务器从中获取到目标地址,从而实现转发。

之前还以为是通过Host请求头获取目标地址的,然后试着Nginx反代配置中直接用proxy-set-header Host xxxx,想着能实现通过正向代理向特定目标转发请求,现在看来这样无法实现。

以上仅为个人的浅薄分析,如有错误请指正。