HTTP被动扫描代理的那些事
字数 1855 2025-08-18 11:38:56

HTTP被动扫描代理技术详解

1. HTTP代理基础概念

HTTP代理是一种中间人(MITM)技术,用于截获、修改和转发HTTP/HTTPS请求。安全测试工具如Burp Suite就是通过配置HTTP代理来实现请求拦截。

1.1 代理环境变量

  • HTTP_PROXY:用于HTTP请求的代理设置
  • HTTPS_PROXY:用于HTTPS请求的代理设置
  • 环境变量值格式:
    • http://127.0.0.1:7777
    • https://127.0.0.1:7777
    • socks5://127.0.0.1:7777

注意:许多工具不支持https://类型的代理地址,会将其当作http://处理。

2. HTTP代理工作原理

2.1 普通HTTP代理模式

当客户端通过代理访问HTTP站点时,代理会收到如下格式的请求:

GET http://example.com/ HTTP/1.1
Host: example.com
Proxy-Connection: keep-alive
...

关键区别:GET后是完整URI而非路径,以便代理获取原始请求信息。

2.2 简易HTTP代理实现(Go语言示例)

package main

import (
    "bufio"
    "log"
    "net"
    "net/http"
)

var client = http.Client{}

func main() {
    listener, err := net.Listen("tcp", "127.0.0.1:7777")
    if err != nil {
        log.Fatal(err)
    }
    
    for {
        conn, err := listener.Accept()
        if err != nil {
            log.Fatal(err)
        }
        go handleConn(conn)
    }
}

func handleConn(conn net.Conn) {
    // 读取代理请求
    req, err := http.ReadRequest(bufio.NewReader(conn))
    if err != nil {
        log.Println(err)
        return
    }
    
    req.RequestURI = ""
    // 发送请求获取响应
    resp, err := client.Do(req)
    if err != nil {
        log.Println(err)
        return
    }
    
    // 返回响应给客户端
    _ = resp.Write(conn)
    _ = conn.Close()
}

3. HTTPS与隧道代理

3.1 CONNECT方法

当客户端通过代理访问HTTPS站点时,会先发送CONNECT请求:

CONNECT baidu.com:443 HTTP/1.1
Host: baidu.com:443
User-Agent: curl/7.54.0
Proxy-Connection: Keep-Alive

3.2 隧道代理工作流程

  1. 客户端发送CONNECT请求到代理
  2. 代理尝试连接目标服务器
    • 成功:返回200响应,连接控制权转交客户端
    • 失败:返回502,连接中止
  3. 客户端收到200后,在该连接中进行TLS握手
  4. 握手成功后进行正常HTTP传输

3.3 隧道流量识别

隧道中可以传输任意数据,识别TLS流量的方法:

  • TLS协议以0x16(ClientHello)开头
  • 非TLS流量视为HTTP流量

伪代码实现:

b = conn.Read(1)
if b == "0x16" {
    tlsHandShake(conn)
}
req = readRequest(conn)
handleReq(conn, req)

注意:读取的第一个字节需要"塞回去",否则后续操作会失败。

4. TLS中间人(MITM)技术

4.1 证书信任机制

  • 根证书(RootCA) > 中间证书(Intermediates CA) > 终端用户证书(End-User Cert)
  • 信任根证书后,其下级签发的证书都会被自动信任
  • 系统/浏览器内置了权威机构的根证书

4.2 MITM实现原理

  1. 代理拥有被系统信任的根证书
  2. 针对每个HTTPS连接,代理动态签发对应域的证书
  3. 使用签发的证书与客户端完成TLS握手
  4. 剥开TLS层获取明文HTTP请求

注意:如果不需要中间人获取请求,可以不信任证书,仅做数据转发。

5. 代理实现细节

5.1 代理认证

代理认证使用Proxy-Authenticate和Proxy-Authorization头,类似于HTTP Basic Auth但加了Proxy-前缀。

5.2 单跳头(Hop-By-Hop header)处理

以下头应当被代理删除:

"Proxy-Authenticate", "Proxy-Authorization", "Connection", 
"Keep-Alive", "Proxy-Connection", "Te", "Trailer", 
"Transfer-Encoding", "Upgrade"

5.3 连接状态管理

  • client->proxy:每次新建TCP连接,不复用
  • proxy->server:使用连接池复用TCP连接
  • 串联两部分连接(Go示例):
go io.Copy(conn1, conn2)
io.Copy(conn2, conn1)

5.4 特殊协议处理

  • Websocket:直接放行,不解析(仅做数据转发)
  • HTTP2:拒绝协议转换,强制使用HTTP

6. 现有实现的不完美之处

  1. 隧道模式下的协议识别:

    • 仅以0x16判断TLS流量可能有误判
    • 假设TLS层下一定是HTTP协议可能不准确
  2. 证书签发流程:

    • 直接从CONNECT的HOST获取域名不够精确
    • 更好的方法是从TLS握手中读取SNI(Server Name Indication)信息

7. 参考实现

  • Python的MitmProxy:实现最全面科学
  • Go语言目前缺乏类似MitmProxy的完善实现

8. 关键问题解答

  1. http_proxy和https_proxy有什么区别?

    • http_proxy用于HTTP请求,https_proxy用于HTTPS请求
    • 这是约定俗成的规则,并非RFC强制规定
  2. 为什么需要信任证书才能扫描HTTPS站点?

    • 代理需要动态签发目标站点证书进行中间人攻击
    • 只有信任了根证书,签发的证书才会被客户端接受
  3. 代理HTTPS站点一定需要信任证书吗?

    • 不需要,如果仅做隧道转发而不解析内容,可以不信任证书
  4. 隧道模式下如何区分TLS流量?

    • 检查第一个字节是否为0x16(ClientHello)
    • 注意读取后要将字节"塞回去"
  5. 如何处理Websocket和HTTP2流量?

    • Websocket:直接放行,不解析
    • HTTP2:拒绝协议转换,强制使用HTTP
  6. 是否应该复用连接以及如何复用?

    • client->proxy:不复用
    • proxy->server:使用连接池复用
    • 两部分连接分别管理状态后串联
HTTP被动扫描代理技术详解 1. HTTP代理基础概念 HTTP代理是一种中间人(MITM)技术,用于截获、修改和转发HTTP/HTTPS请求。安全测试工具如Burp Suite就是通过配置HTTP代理来实现请求拦截。 1.1 代理环境变量 HTTP_PROXY :用于HTTP请求的代理设置 HTTPS_PROXY :用于HTTPS请求的代理设置 环境变量值格式: http://127.0.0.1:7777 https://127.0.0.1:7777 socks5://127.0.0.1:7777 注意:许多工具不支持 https:// 类型的代理地址,会将其当作 http:// 处理。 2. HTTP代理工作原理 2.1 普通HTTP代理模式 当客户端通过代理访问HTTP站点时,代理会收到如下格式的请求: 关键区别:GET后是完整URI而非路径,以便代理获取原始请求信息。 2.2 简易HTTP代理实现(Go语言示例) 3. HTTPS与隧道代理 3.1 CONNECT方法 当客户端通过代理访问HTTPS站点时,会先发送CONNECT请求: 3.2 隧道代理工作流程 客户端发送CONNECT请求到代理 代理尝试连接目标服务器 成功:返回200响应,连接控制权转交客户端 失败:返回502,连接中止 客户端收到200后,在该连接中进行TLS握手 握手成功后进行正常HTTP传输 3.3 隧道流量识别 隧道中可以传输任意数据,识别TLS流量的方法: TLS协议以0x16(ClientHello)开头 非TLS流量视为HTTP流量 伪代码实现: 注意:读取的第一个字节需要"塞回去",否则后续操作会失败。 4. TLS中间人(MITM)技术 4.1 证书信任机制 根证书(RootCA) > 中间证书(Intermediates CA) > 终端用户证书(End-User Cert) 信任根证书后,其下级签发的证书都会被自动信任 系统/浏览器内置了权威机构的根证书 4.2 MITM实现原理 代理拥有被系统信任的根证书 针对每个HTTPS连接,代理动态签发对应域的证书 使用签发的证书与客户端完成TLS握手 剥开TLS层获取明文HTTP请求 注意:如果不需要中间人获取请求,可以不信任证书,仅做数据转发。 5. 代理实现细节 5.1 代理认证 代理认证使用Proxy-Authenticate和Proxy-Authorization头,类似于HTTP Basic Auth但加了Proxy-前缀。 5.2 单跳头(Hop-By-Hop header)处理 以下头应当被代理删除: 5.3 连接状态管理 client->proxy:每次新建TCP连接,不复用 proxy->server:使用连接池复用TCP连接 串联两部分连接(Go示例): 5.4 特殊协议处理 Websocket :直接放行,不解析(仅做数据转发) HTTP2 :拒绝协议转换,强制使用HTTP 6. 现有实现的不完美之处 隧道模式下的协议识别: 仅以0x16判断TLS流量可能有误判 假设TLS层下一定是HTTP协议可能不准确 证书签发流程: 直接从CONNECT的HOST获取域名不够精确 更好的方法是从TLS握手中读取SNI(Server Name Indication)信息 7. 参考实现 Python的MitmProxy:实现最全面科学 Go语言目前缺乏类似MitmProxy的完善实现 8. 关键问题解答 http_ proxy和https_ proxy有什么区别? http_ proxy用于HTTP请求,https_ proxy用于HTTPS请求 这是约定俗成的规则,并非RFC强制规定 为什么需要信任证书才能扫描HTTPS站点? 代理需要动态签发目标站点证书进行中间人攻击 只有信任了根证书,签发的证书才会被客户端接受 代理HTTPS站点一定需要信任证书吗? 不需要,如果仅做隧道转发而不解析内容,可以不信任证书 隧道模式下如何区分TLS流量? 检查第一个字节是否为0x16(ClientHello) 注意读取后要将字节"塞回去" 如何处理Websocket和HTTP2流量? Websocket:直接放行,不解析 HTTP2:拒绝协议转换,强制使用HTTP 是否应该复用连接以及如何复用? client->proxy:不复用 proxy->server:使用连接池复用 两部分连接分别管理状态后串联