├── .gitignore ├── .vscode └── settings.json ├── README.md ├── build.sh └── src ├── main └── main.go └── vendor ├── config └── config.go └── mitm ├── cache.go ├── dump.go ├── keyman.go ├── listener.go ├── mitmproxy.go └── mitmserver.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.dll 4 | *.so 5 | *.dylib 6 | 7 | # Test binary, build with `go test -c` 8 | *.test 9 | 10 | # Output of the go coverage tool, specifically when used with LiteIDE 11 | *.out 12 | 13 | # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 14 | .glide/ 15 | 16 | # go specific 17 | pkg 18 | bin 19 | debug 20 | *.log 21 | *.pem 22 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "go.inferGopath": true 3 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mitm proxy 2 | 3 | ## Introduction 4 | 5 | 本项目实现了一个 Man-in-the-middle 代理服务器。支持 HTTP 和 HTTPS 抓包。 6 | 7 | It is a Man-in-the-middle proxy server, supports to capture HTTP/HTTPS request and response. 8 | 9 | ## How 10 | 11 | 对于 HTTP,直接修改头部,并转发请求。 12 | 13 | 而对于 HTTPS,则是在 CONNECT 请求所建立的 TCP 隧道中,又插入了一个 TLS 服务,并由该 TLS 服务先解析成明文,再进行 TLS 请求。该插入的 TLS 服务器需要为每个域名实时签发证书,故浏览器会提示证书错误。 14 | 15 | ## 详细原理介绍 16 | 17 | 详细原理在我的博客文章:[实现基于 HTTPS 代理的中间人攻击](http://www.lyyyuna.com/2018/03/16/http-proxy-https/) 18 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | os=$1 2 | arch=$2 3 | 4 | export GOPATH=`pwd` 5 | GOOS=$os GOARCH=$arch go build -o bin/httpmitm$os$arch src/main/*.go 6 | -------------------------------------------------------------------------------- /src/main/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "config" 5 | "flag" 6 | "mitm" 7 | ) 8 | 9 | func main() { 10 | 11 | conf := new(config.Cfg) 12 | conf.Port = flag.String("port", "8080", "Listen port") 13 | // conf.Addr = flag.String("addr", "127.0.0.1", "Listening IP address") 14 | conf.Log = flag.String("log", "mitm.log", "Specify the log path") 15 | 16 | flag.Parse() 17 | 18 | ch := make(chan bool) 19 | mitm.Gomitmproxy(conf, ch) 20 | <-ch 21 | } 22 | -------------------------------------------------------------------------------- /src/vendor/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import "crypto/tls" 4 | 5 | // Cfg Configuration for mitm 6 | type Cfg struct { 7 | Port *string 8 | Log *string 9 | } 10 | 11 | // TLSConfig Configuration for TLS 12 | type TLSConfig struct { 13 | PrivateKeyFile string 14 | CertFile string 15 | Organization string 16 | CommonName string 17 | ServerTLSConfig *tls.Config 18 | } 19 | 20 | // NewTLSConfig init the TlsConfig 21 | func NewTLSConfig(pk, cert, org, cn string) *TLSConfig { 22 | return &TLSConfig{ 23 | PrivateKeyFile: pk, 24 | CertFile: cert, 25 | Organization: org, 26 | CommonName: cn, 27 | ServerTLSConfig: &tls.Config{ 28 | CipherSuites: []uint16{ 29 | tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, 30 | tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, 31 | tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA, 32 | tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, 33 | tls.TLS_RSA_WITH_RC4_128_SHA, 34 | tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA, 35 | tls.TLS_RSA_WITH_AES_128_CBC_SHA, 36 | tls.TLS_RSA_WITH_AES_256_CBC_SHA, 37 | tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 38 | tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 39 | }, 40 | PreferServerCipherSuites: true, 41 | InsecureSkipVerify: true, 42 | }, 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/vendor/mitm/cache.go: -------------------------------------------------------------------------------- 1 | // package cache implements a really primitive cache that associates expiring 2 | // values with string keys. This cache never clears itself out. 3 | package mitm 4 | 5 | import ( 6 | "sync" 7 | "time" 8 | ) 9 | 10 | // Cache is a cache for binary data 11 | type Cache struct { 12 | entries map[string]*entry 13 | mutex *sync.Mutex 14 | } 15 | 16 | // entry is an entry in a Cache 17 | type entry struct { 18 | data interface{} 19 | expiration time.Time 20 | } 21 | 22 | // NewCache creates a new Cache 23 | func NewCache() *Cache { 24 | c := &Cache{} 25 | c.entries = make(map[string]*entry) 26 | c.mutex = &sync.Mutex{} 27 | return c 28 | } 29 | 30 | // Get returns the currently cached value for the given key, as long as it 31 | // hasn't expired. If the key was never set, or has expired, found will be 32 | // false. 33 | func (cache *Cache) Get(key string) (val interface{}, found bool) { 34 | cache.mutex.Lock() 35 | defer cache.mutex.Unlock() 36 | entry := cache.entries[key] 37 | if entry == nil { 38 | return nil, false 39 | } else if entry.expiration.Before(time.Now()) { 40 | return nil, false 41 | } else { 42 | return entry.data, true 43 | } 44 | } 45 | 46 | // Set sets a value in the cache with an expiration of now + ttl. 47 | func (cache *Cache) Set(key string, data interface{}, ttl time.Duration) { 48 | cache.mutex.Lock() 49 | defer cache.mutex.Unlock() 50 | cache.entries[key] = &entry{data, time.Now().Add(ttl)} 51 | } 52 | -------------------------------------------------------------------------------- /src/vendor/mitm/dump.go: -------------------------------------------------------------------------------- 1 | package mitm 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "fmt" 7 | "io" 8 | "net/http" 9 | ) 10 | 11 | func httpDump(reqDump []byte, resp *http.Response) { 12 | defer resp.Body.Close() 13 | respStatus := resp.Status 14 | 15 | req, _ := ParseReq(reqDump) 16 | 17 | fmt.Println("Status: ", respStatus) 18 | fmt.Printf("%s %s %s\n", req.Method, req.Host, req.RequestURI) 19 | fmt.Printf("%s %s\n", "Remote Addr: ", req.RemoteAddr) 20 | for headerName, headerContext := range req.Header { 21 | fmt.Printf("\t%s: %s\n", headerName, headerContext) 22 | } 23 | 24 | if req.Method == "POST" { 25 | fmt.Println("POST Param:") 26 | err := req.ParseForm() 27 | if err != nil { 28 | logger.Println("ParseForm error: ", err) 29 | } else { 30 | for k, v := range req.Form { 31 | fmt.Printf("\t\t%s: %s\n", k, v) 32 | } 33 | } 34 | } 35 | 36 | fmt.Println("Response:") 37 | for headerName, headerContext := range resp.Header { 38 | fmt.Printf("\t%s: %s\n", headerName, headerContext) 39 | } 40 | fmt.Println("") 41 | } 42 | 43 | // ParseReq why 44 | func ParseReq(b []byte) (*http.Request, error) { 45 | // func ReadRequest(b *bufio.Reader) (req *Request, err error) { return readRequest(b, deleteHostHeader) } 46 | var buf io.ReadWriter 47 | buf = new(bytes.Buffer) 48 | buf.Write(b) 49 | bufr := bufio.NewReader(buf) 50 | return http.ReadRequest(bufr) 51 | } 52 | -------------------------------------------------------------------------------- /src/vendor/mitm/keyman.go: -------------------------------------------------------------------------------- 1 | // Package keyman provides convenience APIs around Go's built-in crypto APIs. 2 | package mitm 3 | 4 | import ( 5 | "crypto/rand" 6 | "crypto/rsa" 7 | "crypto/x509" 8 | "crypto/x509/pkix" 9 | "encoding/pem" 10 | "fmt" 11 | "io/ioutil" 12 | "math/big" 13 | "net" 14 | "os" 15 | "time" 16 | ) 17 | 18 | const ( 19 | PEM_HEADER_PRIVATE_KEY = "RSA PRIVATE KEY" 20 | PEM_HEADER_PUBLIC_KEY = "RSA PRIVATE KEY" 21 | PEM_HEADER_CERTIFICATE = "CERTIFICATE" 22 | ) 23 | 24 | var ( 25 | tenYearsFromToday = time.Now().AddDate(10, 0, 0) 26 | ) 27 | 28 | // PrivateKey is a convenience wrapper for rsa.PrivateKey 29 | type PrivateKey struct { 30 | rsaKey *rsa.PrivateKey 31 | } 32 | 33 | // Certificate is a convenience wrapper for x509.Certificate 34 | type Certificate struct { 35 | cert *x509.Certificate 36 | derBytes []byte 37 | } 38 | 39 | /******************************************************************************* 40 | * Private Key Functions 41 | ******************************************************************************/ 42 | 43 | // GeneratePK generates a PrivateKey with a specified size in bits. 44 | func GeneratePK(bits int) (key *PrivateKey, err error) { 45 | var rsaKey *rsa.PrivateKey 46 | rsaKey, err = rsa.GenerateKey(rand.Reader, bits) 47 | if err == nil { 48 | key = &PrivateKey{rsaKey: rsaKey} 49 | } 50 | return 51 | } 52 | 53 | // LoadPKFromFile loads a PEM-encoded PrivateKey from a file 54 | func LoadPKFromFile(filename string) (key *PrivateKey, err error) { 55 | privateKeyData, err := ioutil.ReadFile(filename) 56 | if err != nil { 57 | if os.IsNotExist(err) { 58 | return nil, err 59 | } 60 | return nil, fmt.Errorf("Unable to read private key file from file %s: %s", filename, err) 61 | } 62 | block, _ := pem.Decode(privateKeyData) 63 | if block == nil { 64 | return nil, fmt.Errorf("Unable to decode PEM encoded private key data: %s", err) 65 | } 66 | rsaKey, err := x509.ParsePKCS1PrivateKey(block.Bytes) 67 | if err != nil { 68 | return nil, fmt.Errorf("Unable to decode X509 private key data: %s", err) 69 | } 70 | return &PrivateKey{rsaKey: rsaKey}, nil 71 | } 72 | 73 | // PEMEncoded encodes the PrivateKey in PEM 74 | func (key *PrivateKey) PEMEncoded() (pemBytes []byte) { 75 | return pem.EncodeToMemory(key.pemBlock()) 76 | } 77 | 78 | // WriteToFile writes the PEM-encoded PrivateKey to the given file 79 | func (key *PrivateKey) WriteToFile(filename string) (err error) { 80 | keyOut, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) 81 | if err != nil { 82 | return fmt.Errorf("Failed to open %s for writing: %s", filename, err) 83 | } 84 | if err := pem.Encode(keyOut, key.pemBlock()); err != nil { 85 | return fmt.Errorf("Unable to PEM encode private key: %s", err) 86 | } 87 | if err := keyOut.Close(); err != nil { 88 | logger.Printf("Unable to close file: %v", err) 89 | } 90 | return 91 | } 92 | 93 | func (key *PrivateKey) pemBlock() *pem.Block { 94 | return &pem.Block{Type: PEM_HEADER_PRIVATE_KEY, Bytes: x509.MarshalPKCS1PrivateKey(key.rsaKey)} 95 | } 96 | 97 | /******************************************************************************* 98 | * Certificate Functions 99 | ******************************************************************************/ 100 | 101 | /* 102 | Certificate() generates a certificate for the Public Key of the given PrivateKey 103 | based on the given template and signed by the given issuer. If issuer is nil, 104 | the generated certificate is self-signed. 105 | */ 106 | func (key *PrivateKey) Certificate(template *x509.Certificate, issuer *Certificate) (*Certificate, error) { 107 | return key.CertificateForKey(template, issuer, &key.rsaKey.PublicKey) 108 | } 109 | 110 | /* 111 | CertificateForKey() generates a certificate for the given Public Key based on 112 | the given template and signed by the given issuer. If issuer is nil, the 113 | generated certificate is self-signed. 114 | */ 115 | func (key *PrivateKey) CertificateForKey(template *x509.Certificate, issuer *Certificate, publicKey interface{}) (*Certificate, error) { 116 | var issuerCert *x509.Certificate 117 | if issuer == nil { 118 | // Note - for self-signed certificates, we include the host's external IP address 119 | issuerCert = template 120 | } else { 121 | issuerCert = issuer.cert 122 | } 123 | derBytes, err := x509.CreateCertificate( 124 | rand.Reader, // secure entropy 125 | template, // the template for the new cert 126 | issuerCert, // cert that's signing this cert 127 | publicKey, // public key 128 | key.rsaKey, // private key 129 | ) 130 | if err != nil { 131 | return nil, err 132 | } 133 | return bytesToCert(derBytes) 134 | } 135 | 136 | // TLSCertificateFor generates a certificate useful for TLS use based on the 137 | // given parameters. These certs are usable for key encipherment and digital 138 | // signatures. 139 | // 140 | // organization: the org name for the cert. 141 | // name: used as the common name for the cert. If name is an IP 142 | // address, it is also added as an IP SAN. 143 | // validUntil: time at which certificate expires 144 | // isCA: whether or not this cert is a CA 145 | // issuer: the certificate which is issuing the new cert. If nil, the 146 | // new cert will be a self-signed CA certificate. 147 | // 148 | func (key *PrivateKey) TLSCertificateFor( 149 | organization string, 150 | name string, 151 | validUntil time.Time, 152 | isCA bool, 153 | issuer *Certificate) (cert *Certificate, err error) { 154 | 155 | template := &x509.Certificate{ 156 | SerialNumber: new(big.Int).SetInt64(int64(time.Now().UnixNano())), 157 | Subject: pkix.Name{ 158 | Organization: []string{organization}, 159 | CommonName: name, 160 | }, 161 | NotBefore: time.Now().AddDate(0, -1, 0), 162 | NotAfter: validUntil, 163 | 164 | BasicConstraintsValid: true, 165 | KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, 166 | } 167 | 168 | // If name is an ip address, add it as an IP SAN 169 | ip := net.ParseIP(name) 170 | if ip != nil { 171 | template.IPAddresses = []net.IP{ip} 172 | } 173 | 174 | isSelfSigned := issuer == nil 175 | if isSelfSigned { 176 | template.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth} 177 | } 178 | 179 | // If it's a CA, add certificate signing 180 | if isCA { 181 | template.KeyUsage = template.KeyUsage | x509.KeyUsageCertSign 182 | template.IsCA = true 183 | } 184 | 185 | cert, err = key.Certificate(template, issuer) 186 | return 187 | } 188 | 189 | // LoadCertificateFromFile loads a Certificate from a PEM-encoded file 190 | func LoadCertificateFromFile(filename string) (*Certificate, error) { 191 | certificateData, err := ioutil.ReadFile(filename) 192 | if err != nil { 193 | if os.IsNotExist(err) { 194 | return nil, err 195 | } 196 | return nil, fmt.Errorf("Unable to read certificate file from disk: %s", err) 197 | } 198 | return LoadCertificateFromPEMBytes(certificateData) 199 | } 200 | 201 | // LoadCertificateFromPEMBytes loads a Certificate from a byte array in PEM 202 | // format 203 | func LoadCertificateFromPEMBytes(pemBytes []byte) (*Certificate, error) { 204 | block, _ := pem.Decode(pemBytes) 205 | if block == nil { 206 | return nil, fmt.Errorf("Unable to decode PEM encoded certificate") 207 | } 208 | return bytesToCert(block.Bytes) 209 | } 210 | 211 | // LoadCertificateFromX509 loads a Certificate from an x509.Certificate 212 | func LoadCertificateFromX509(cert *x509.Certificate) (*Certificate, error) { 213 | pemBytes := pem.EncodeToMemory(&pem.Block{ 214 | Type: "CERTIFICATE", 215 | Headers: nil, 216 | Bytes: cert.Raw, 217 | }) 218 | return LoadCertificateFromPEMBytes(pemBytes) 219 | } 220 | 221 | // X509 returns the x509 certificate underlying this Certificate 222 | func (cert *Certificate) X509() *x509.Certificate { 223 | return cert.cert 224 | } 225 | 226 | // PEMEncoded encodes the Certificate in PEM 227 | func (cert *Certificate) PEMEncoded() (pemBytes []byte) { 228 | return pem.EncodeToMemory(cert.pemBlock()) 229 | } 230 | 231 | // WriteToFile writes the PEM-encoded Certificate to a file. 232 | func (cert *Certificate) WriteToFile(filename string) (err error) { 233 | certOut, err := os.Create(filename) 234 | if err != nil { 235 | return fmt.Errorf("Failed to open %s for writing: %s", filename, err) 236 | } 237 | defer func() { 238 | if err := certOut.Close(); err != nil { 239 | logger.Printf("Unable to close file: %v", err) 240 | } 241 | }() 242 | return pem.Encode(certOut, cert.pemBlock()) 243 | } 244 | 245 | func (cert *Certificate) WriteToTempFile() (name string, err error) { 246 | // Create a temp file containing the certificate 247 | tempFile, err := ioutil.TempFile("", "tempCert") 248 | if err != nil { 249 | return "", fmt.Errorf("Unable to create temp file: %s", err) 250 | } 251 | name = tempFile.Name() 252 | err = cert.WriteToFile(name) 253 | if err != nil { 254 | return "", fmt.Errorf("Unable to save certificate to temp file: %s", err) 255 | } 256 | return 257 | } 258 | 259 | // WriteToDERFile writes the DER-encoded Certificate to a file. 260 | func (cert *Certificate) WriteToDERFile(filename string) (err error) { 261 | certOut, err := os.Create(filename) 262 | if err != nil { 263 | return fmt.Errorf("Failed to open %s for writing: %s", filename, err) 264 | } 265 | defer func() { 266 | if err := certOut.Close(); err != nil { 267 | logger.Printf("Unable to close file: %v", err) 268 | } 269 | }() 270 | _, err = certOut.Write(cert.derBytes) 271 | return err 272 | } 273 | 274 | // PoolContainingCert creates a pool containing this cert. 275 | func (cert *Certificate) PoolContainingCert() *x509.CertPool { 276 | pool := x509.NewCertPool() 277 | pool.AddCert(cert.cert) 278 | return pool 279 | } 280 | 281 | // PoolContainingCerts constructs a CertPool containing all of the given certs 282 | // (PEM encoded). 283 | func PoolContainingCerts(certs ...string) (*x509.CertPool, error) { 284 | pool := x509.NewCertPool() 285 | for _, cert := range certs { 286 | c, err := LoadCertificateFromPEMBytes([]byte(cert)) 287 | if err != nil { 288 | return nil, err 289 | } 290 | pool.AddCert(c.cert) 291 | } 292 | return pool, nil 293 | } 294 | 295 | func (cert *Certificate) ExpiresBefore(time time.Time) bool { 296 | return cert.cert.NotAfter.Before(time) 297 | } 298 | 299 | func bytesToCert(derBytes []byte) (*Certificate, error) { 300 | cert, err := x509.ParseCertificate(derBytes) 301 | if err != nil { 302 | return nil, err 303 | } 304 | return &Certificate{cert, derBytes}, nil 305 | } 306 | 307 | func (cert *Certificate) pemBlock() *pem.Block { 308 | return &pem.Block{Type: PEM_HEADER_CERTIFICATE, Bytes: cert.derBytes} 309 | } 310 | -------------------------------------------------------------------------------- /src/vendor/mitm/listener.go: -------------------------------------------------------------------------------- 1 | package mitm 2 | 3 | import ( 4 | "io" 5 | "net" 6 | ) 7 | 8 | type mitmListener struct { 9 | conn net.Conn 10 | } 11 | 12 | func (listener *mitmListener) Accept() (net.Conn, error) { 13 | if listener.conn != nil { 14 | conn := listener.conn 15 | listener.conn = nil 16 | return conn, nil 17 | } else { 18 | return nil, io.EOF 19 | } 20 | } 21 | 22 | func (listener *mitmListener) Close() error { 23 | return nil 24 | } 25 | 26 | func (listener *mitmListener) Addr() net.Addr { 27 | return nil 28 | } 29 | -------------------------------------------------------------------------------- /src/vendor/mitm/mitmproxy.go: -------------------------------------------------------------------------------- 1 | package mitm 2 | 3 | import ( 4 | "config" 5 | "log" 6 | "net/http" 7 | "os" 8 | "time" 9 | ) 10 | 11 | var logger *log.Logger 12 | 13 | // Gomitmproxy create a mitm proxy and start it 14 | func Gomitmproxy(conf *config.Cfg, ch chan bool) { 15 | tlsConfig := config.NewTLSConfig("ca-pk.pem", "ca-cert.pem", "", "") 16 | handler := InitConfig(conf, tlsConfig) 17 | server := &http.Server{ 18 | Addr: ":" + *conf.Port, 19 | ReadTimeout: 1 * time.Hour, 20 | WriteTimeout: 1 * time.Hour, 21 | Handler: handler, 22 | } 23 | 24 | l, _ := os.Create(*conf.Log) 25 | logger = log.New(l, "[mitmproxy]", log.Ldate|log.Ltime|log.Lmicroseconds|log.Lshortfile) 26 | logger.Println("Server is listening at ", server.Addr) 27 | 28 | go func() { 29 | server.ListenAndServe() 30 | ch <- true 31 | }() 32 | 33 | return 34 | } 35 | -------------------------------------------------------------------------------- /src/vendor/mitm/mitmserver.go: -------------------------------------------------------------------------------- 1 | package mitm 2 | 3 | import ( 4 | "bufio" 5 | "config" 6 | "crypto/tls" 7 | "fmt" 8 | "io" 9 | "net" 10 | "net/http" 11 | "net/http/httputil" 12 | "regexp" 13 | "strings" 14 | "sync" 15 | "time" 16 | ) 17 | 18 | const ( 19 | Version = "1.1" 20 | ONE_DAY = 24 * time.Hour 21 | TWO_WEEKS = ONE_DAY * 14 22 | ONE_MONTH = 1 23 | ONE_YEAR = 1 24 | ) 25 | 26 | // HandlerWrapper wrapper of handler for http server 27 | type HandlerWrapper struct { 28 | Config *config.Cfg 29 | wrapped http.Handler 30 | tlsConfig *config.TLSConfig 31 | pk *PrivateKey 32 | pkPem []byte 33 | issuingCert *Certificate 34 | issuingCertPem []byte 35 | dynamicCerts *Cache 36 | certMutex sync.Mutex 37 | https bool 38 | } 39 | 40 | // InitConfig init HandlerWrapper 41 | func InitConfig(conf *config.Cfg, tlsconfig *config.TLSConfig) *HandlerWrapper { 42 | handler := &HandlerWrapper{ 43 | Config: conf, 44 | tlsConfig: tlsconfig, 45 | dynamicCerts: NewCache(), 46 | } 47 | err := handler.GenerateCertForClient() 48 | if err != nil { 49 | return nil 50 | } 51 | return handler 52 | } 53 | 54 | // ServeHTTP the main function interface for http handler 55 | func (handler *HandlerWrapper) ServeHTTP(resp http.ResponseWriter, req *http.Request) { 56 | if req.Method == "CONNECT" { 57 | handler.https = true 58 | handler.InterceptHTTPS(resp, req) 59 | } else { 60 | handler.https = false 61 | handler.DumpHTTPAndHTTPS(resp, req) 62 | } 63 | } 64 | 65 | // DumpHTTPAndHTTPS function to dump the HTTP request header and body 66 | func (handler *HandlerWrapper) DumpHTTPAndHTTPS(resp http.ResponseWriter, req *http.Request) { 67 | req.Header.Del("Proxy-Connection") 68 | req.Header.Set("Connection", "Keep-Alive") 69 | 70 | var reqDump []byte 71 | ch := make(chan bool) 72 | // handle connection 73 | go func() { 74 | reqDump, _ = httputil.DumpRequestOut(req, true) 75 | ch <- true 76 | }() 77 | 78 | connHj, _, err := resp.(http.Hijacker).Hijack() 79 | if err != nil { 80 | logger.Println("Hijack fail to take over the TCP connection from client's request") 81 | } 82 | defer connHj.Close() 83 | 84 | host := req.Host 85 | 86 | matched, _ := regexp.MatchString(":[0-9]+$", host) 87 | 88 | var connOut net.Conn 89 | if !handler.https { 90 | if !matched { 91 | host += ":80" 92 | } 93 | connOut, err = net.DialTimeout("tcp", host, time.Second*30) 94 | if err != nil { 95 | logger.Println("Dial to", host, "error:", err) 96 | return 97 | } 98 | } else { 99 | if !matched { 100 | host += ":443" 101 | } 102 | connOut, err = tls.Dial("tcp", host, handler.tlsConfig.ServerTLSConfig) 103 | if err != nil { 104 | logger.Println("Dial to", host, "error:", err) 105 | return 106 | } 107 | } 108 | 109 | // Write writes an HTTP/1.1 request, which is the header and body, in wire format. This method consults the following fields of the request: 110 | /* 111 | Host 112 | URL 113 | Method (defaults to "GET") 114 | Header 115 | ContentLength 116 | TransferEncoding 117 | Body 118 | */ 119 | if err = req.Write(connOut); err != nil { 120 | logger.Println("send to server error", err) 121 | return 122 | } 123 | 124 | respFromRemote, err := http.ReadResponse(bufio.NewReader(connOut), req) 125 | if err != nil && err != io.EOF { 126 | logger.Println("Fail to read response from remote server.", err) 127 | } 128 | 129 | respDump, err := httputil.DumpResponse(respFromRemote, true) 130 | if err != nil { 131 | logger.Println("Fail to dump the response.", err) 132 | } 133 | // Send remote response back to client 134 | _, err = connHj.Write(respDump) 135 | if err != nil { 136 | logger.Println("Fail to send response back to client.", err) 137 | } 138 | 139 | <-ch 140 | // why write to reqDump, and in httpDump resemble to req again 141 | // in test, i find that the req may be destroyed by sth i currently dont know 142 | // so while parsing req in httpDump directly, it will raise execption 143 | // so dump its content to reqDump first. 144 | go httpDump(reqDump, respFromRemote) 145 | } 146 | 147 | // InterceptHTTPS to dump data in HTTPS 148 | func (handler *HandlerWrapper) InterceptHTTPS(resp http.ResponseWriter, req *http.Request) { 149 | addr := req.Host 150 | host := strings.Split(addr, ":")[0] 151 | 152 | cert, err := handler.FakeCertForName(host) 153 | if err != nil { 154 | logger.Println("Could not get mitm cert for name: %s\nerror: %s", host, err) 155 | respBadGateway(resp) 156 | return 157 | } 158 | 159 | connIn, _, err := resp.(http.Hijacker).Hijack() 160 | if err != nil { 161 | logger.Println("Unable to access underlying connection from client: %s", err) 162 | respBadGateway(resp) 163 | return 164 | } 165 | 166 | tlsConfig := copyTlsConfig(handler.tlsConfig.ServerTLSConfig) 167 | tlsConfig.Certificates = []tls.Certificate{*cert} 168 | tlsConnIn := tls.Server(connIn, tlsConfig) 169 | listener := &mitmListener{tlsConnIn} 170 | httpshandler := http.HandlerFunc(func(resp2 http.ResponseWriter, req2 *http.Request) { 171 | req2.URL.Scheme = "https" 172 | req2.URL.Host = req2.Host 173 | handler.DumpHTTPAndHTTPS(resp2, req2) 174 | }) 175 | 176 | go func() { 177 | err = http.Serve(listener, httpshandler) 178 | if err != nil && err != io.EOF { 179 | logger.Printf("Error serving mitm'ed connection: %s", err) 180 | } 181 | }() 182 | 183 | connIn.Write([]byte("HTTP/1.1 200 Connection Established\r\n\r\n")) 184 | } 185 | 186 | func (hw *HandlerWrapper) GenerateCertForClient() (err error) { 187 | if hw.tlsConfig.Organization == "" { 188 | hw.tlsConfig.Organization = "gomitmproxy" + Version 189 | } 190 | if hw.tlsConfig.CommonName == "" { 191 | hw.tlsConfig.CommonName = "gomitmproxy" 192 | } 193 | if hw.pk, err = LoadPKFromFile(hw.tlsConfig.PrivateKeyFile); err != nil { 194 | hw.pk, err = GeneratePK(2048) 195 | if err != nil { 196 | return fmt.Errorf("Unable to generate private key: %s", err) 197 | } 198 | hw.pk.WriteToFile(hw.tlsConfig.PrivateKeyFile) 199 | } 200 | hw.pkPem = hw.pk.PEMEncoded() 201 | hw.issuingCert, err = LoadCertificateFromFile(hw.tlsConfig.CertFile) 202 | if err != nil || hw.issuingCert.ExpiresBefore(time.Now().AddDate(0, ONE_MONTH, 0)) { 203 | hw.issuingCert, err = hw.pk.TLSCertificateFor( 204 | hw.tlsConfig.Organization, 205 | hw.tlsConfig.CommonName, 206 | time.Now().AddDate(ONE_YEAR, 0, 0), 207 | true, 208 | nil) 209 | if err != nil { 210 | return fmt.Errorf("Unable to generate self-signed issuing certificate: %s", err) 211 | } 212 | hw.issuingCert.WriteToFile(hw.tlsConfig.CertFile) 213 | } 214 | hw.issuingCertPem = hw.issuingCert.PEMEncoded() 215 | return 216 | } 217 | 218 | func (hw *HandlerWrapper) FakeCertForName(name string) (cert *tls.Certificate, err error) { 219 | kpCandidateIf, found := hw.dynamicCerts.Get(name) 220 | if found { 221 | return kpCandidateIf.(*tls.Certificate), nil 222 | } 223 | 224 | hw.certMutex.Lock() 225 | defer hw.certMutex.Unlock() 226 | kpCandidateIf, found = hw.dynamicCerts.Get(name) 227 | if found { 228 | return kpCandidateIf.(*tls.Certificate), nil 229 | } 230 | 231 | //create certificate 232 | certTTL := TWO_WEEKS 233 | generatedCert, err := hw.pk.TLSCertificateFor( 234 | hw.tlsConfig.Organization, 235 | name, 236 | time.Now().Add(certTTL), 237 | false, 238 | hw.issuingCert) 239 | if err != nil { 240 | return nil, fmt.Errorf("Unable to issue certificate: %s", err) 241 | } 242 | keyPair, err := tls.X509KeyPair(generatedCert.PEMEncoded(), hw.pkPem) 243 | if err != nil { 244 | return nil, fmt.Errorf("Unable to parse keypair for tls: %s", err) 245 | } 246 | 247 | cacheTTL := certTTL - ONE_DAY 248 | hw.dynamicCerts.Set(name, &keyPair, cacheTTL) 249 | return &keyPair, nil 250 | } 251 | 252 | func copyTlsConfig(template *tls.Config) *tls.Config { 253 | tlsConfig := &tls.Config{} 254 | if template != nil { 255 | *tlsConfig = *template 256 | } 257 | return tlsConfig 258 | } 259 | 260 | func copyHTTPRequest(template *http.Request) *http.Request { 261 | req := &http.Request{} 262 | if template != nil { 263 | *req = *template 264 | } 265 | return req 266 | } 267 | 268 | func respBadGateway(resp http.ResponseWriter) { 269 | resp.WriteHeader(502) 270 | } 271 | --------------------------------------------------------------------------------