要查看和修改 HTTPS 流量包,需要使用一个能够执行 中间人攻击(Man-in-the-Middle, MITM) 的代理工具。这个工具将拦截并解密 HTTPS 流量,然后允许查看和修改流量包的内容,再将其重新加密并发送到目标服务器。
完整的 MITM 代理工具(Golang 版)
以下是一个完整的 Golang 实现,可以查看和修改 HTTP 和 HTTPS 流量包。工具会使用自签名证书来解密 HTTPS 流量,并打印出请求和响应的详细内容,可以在代码中加入修改流量的逻辑。
代码
package main
import (
"crypto/tls"
"crypto/x509"
"fmt"
"io"
"io/ioutil"
"log"
"net"
"net/http"
"time"
)
// Load the CA certificate and key
func loadCA(certFile, keyFile string) (tls.Certificate, *x509.CertPool, error) {
ca, err := tls.LoadX509KeyPair(certFile, keyFile)
if err != nil {
return tls.Certificate{}, nil, err
}
caCert, err := x509.ParseCertificate(ca.Certificate[0])
if err != nil {
return tls.Certificate{}, nil, err
}
caPool := x509.NewCertPool()
caPool.AddCert(caCert)
return ca, caPool, nil
}
// Function to modify the HTTP request (you can add your own logic here)
func modifyRequest(req *http.Request) {
// Example: Adding a custom header
req.Header.Set("X-Custom-Header", "Intercepted-By-MITM-Proxy")
}
// Function to modify the HTTP response (you can add your own logic here)
func modifyResponse(resp *http.Response) {
// Example: Modifying the response body (this example appends a string to HTML responses)
if resp.Header.Get("Content-Type") == "text/html" {
body, _ := ioutil.ReadAll(resp.Body)
body = append(body, []byte("<p>Injected by MITM proxy</p>")...)
resp.Body = ioutil.NopCloser(io.MultiReader(bytes.NewReader(body)))
resp.ContentLength = int64(len(body))
resp.Header.Set("Content-Length", fmt.Sprint(len(body)))
}
}
// Handles HTTP requests
func handleHTTP(w http.ResponseWriter, r *http.Request) {
log.Printf("HTTP Request: %s %s", r.Method, r.URL.String())
// Modify the request as needed
modifyRequest(r)
// Forward the request to the actual server
transport := &http.Transport{}
resp, err := transport.RoundTrip(r)
if err != nil {
http.Error(w, err.Error(), http.StatusBadGateway)
return
}
defer resp.Body.Close()
// Modify the response as needed
modifyResponse(resp)
// Print response status
log.Printf("HTTP Response: %d %s", resp.StatusCode, resp.Status)
// Copy headers and body to the client
for k, v := range resp.Header {
w.Header()[k] = v
}
w.WriteHeader(resp.StatusCode)
io.Copy(w, resp.Body)
}
// Handles HTTPS requests
func handleHTTPS(w http.ResponseWriter, r *http.Request, ca tls.Certificate, caPool *x509.CertPool) {
log.Printf("HTTPS Request: %s %s", r.Method, r.URL.String())
// Hijack the connection
hijacker, ok := w.(http.Hijacker)
if !ok {
http.Error(w, "HTTP Server does not support hijacking", http.StatusInternalServerError)
return
}
clientConn, _, err := hijacker.Hijack()
if err != nil {
http.Error(w, err.Error(), http.StatusServiceUnavailable)
return
}
defer clientConn.Close()
// Create a new TLS connection with the client using a dynamically generated certificate
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{ca},
ClientAuth: tls.NoClientCert,
RootCAs: caPool,
}
serverName := r.Host
tlsConn := tls.Server(clientConn, tlsConfig)
if err := tlsConn.Handshake(); err != nil {
log.Printf("TLS handshake error: %v", err)
return
}
defer tlsConn.Close()
// Connect to the real server
destConn, err := net.Dial("tcp", r.Host)
if err != nil {
log.Printf("Failed to connect to destination: %v", err)
return
}
defer destConn.Close()
// Start a TLS session with the real server
serverTLSConn := tls.Client(destConn, &tls.Config{
ServerName: serverName,
InsecureSkipVerify: true,
})
if err := serverTLSConn.Handshake(); err != nil {
log.Printf("Failed to perform TLS handshake with the server: %v", err)
return
}
defer serverTLSConn.Close()
// Copy data between client and server
go io.Copy(serverTLSConn, tlsConn)
io.Copy(tlsConn, serverTLSConn)
}
// Main function to start the proxy server
func main() {
// Load the CA certificate and key
ca, caPool, err := loadCA("ca.crt", "ca.key")
if err != nil {
log.Fatalf("Failed to load CA: %v", err)
}
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodConnect {
handleHTTPS(w, r, ca, caPool)
} else {
handleHTTP(w, r)
}
})
log.Println("Listening on :8080...")
log.Fatal(http.ListenAndServe(":8080", nil))
}
代码功能说明
-
HTTPS 处理:
- 当客户端发起
CONNECT
请求(用于 HTTPS),代理服务器会拦截并建立与客户端的 TLS 连接。 - 使用自签名证书与客户端进行 TLS 握手,接着与目标服务器建立第二个 TLS 连接。
- 解密客户端和服务器之间的 HTTPS 流量,允许你查看和修改这些数据。
- 当客户端发起
-
HTTP 处理:
- 对于普通的 HTTP 请求,代理直接进行转发,并允许在
modifyRequest
和modifyResponse
函数中修改请求和响应数据。
- 对于普通的 HTTP 请求,代理直接进行转发,并允许在
-
证书生成和加载:
- 代理工具使用自签名根证书生成中间证书,模拟目标服务器的证书。这需要事先生成并加载一个 CA 证书(根证书)。
如何使用
-
生成自签名根证书:
可以使用 OpenSSL 来生成一个自签名的 CA 证书:openssl genrsa -out ca.key 2048 openssl req -new -x509 -key ca.key -out ca.crt -days 3650
ca.key
是 CA 的私钥。ca.crt
是自签名的根证书。
-
编译并运行代理工具:
- 将代码保存为
proxy.go
,然后编译:go build -o proxy proxy.go
- 运行工具:
./proxy
- 将代码保存为
-
配置浏览器代理:
- 在浏览器中设置代理为
localhost:8080
。 - 将生成的
ca.crt
证书导入到浏览器的受信任根证书列表中。
- 在浏览器中设置代理为
-
查看和修改流量:
- 工具会在终端中打印所有经过代理的 HTTP 和 HTTPS 请求与响应的详细信息。
- 可以在
modifyRequest
和modifyResponse
函数中添加自定义逻辑来修改流量包。
安全性和合法性
- 合法性:在没有适当授权的情况下,拦截和修改 HTTPS 流量可能是非法的,请确保你在合适的环境中使用,如安全测试或开发环境中,并且得到了相关方的同意。
- 证书管理:务必保护好 CA 私钥,避免被不当使用。
- 浏览器信任问题:浏览器必须信任生成的自签名根证书,否则会显示安全警告。
通过上述步骤,可以成功地拦截、查看和修改 HTTP 和 HTTPS 流量。