برنامه نویسی

Inside AWS S3 API Calls: ایجاد یک بازرس ترافیک HTTPS مبتنی بر GO – Community Dev

آیا تا به حال فکر کرده اید که HTTP در واقع ابزارهای خط فرمان شما چه چیزی را درخواست می کند؟ وقتی دویدید aws s3 ls یا curl https://api.example.com، واقعاً زیر کاپوت چه اتفاقی می افتد؟ در این مقاله ، ابزاری را ایجاد خواهیم کرد که تمام ترافیک HTTP و HTTPS را با ایجاد پروکسی رهگیری خود در GO نشان می دهد.

ما قصد داریم یک ابزار خط فرمان ایجاد کنیم که:

  • درخواست های HTTP و HTTPS را از هر دستور رهگیری می کند
  • ترافیک https را برای نشان دادن محتوای واقعی رمزگشایی می کند
  • جزئیات درخواست و پاسخ فرمت شده را نشان می دهد
  • شفاف با ابزارهایی مانند Curl ، AWS CLI و دیگران کار می کند

استفاده نهایی ساده خواهد بود:

./httpmon curl https://api.github.com
./httpmon aws s3 ls my-bucket
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

درک پروکسی HTTP

قبل از شروع برنامه نویسی ، بیایید درک کنیم که چگونه پروکسی های HTTP کار می کنند. هنگامی که مشتری (مانند CURL) می خواهد از طریق یک پروکسی درخواست HTTP ایجاد کند:

  1. مشتری به جای سرور هدف به پروکسی متصل می شود
  2. برای HTTP: مشتری درخواست کامل را به پروکسی ارسال می کند ، که آن را ارسال می کند
  3. برای HTTPS: مشتری برای ایجاد تونل درخواست اتصال را ارسال می کند
  4. پروکسی درخواست ها و پاسخ ها را بین مشتری و سرور ارسال می کند

چالش با HTTPS این است که رمزگذاری پایان به پایان است. برای دیدن محتوای واقعی ، ما باید آنچه را که “یک مرد در وسط” (MITM) نامیده می شود ، انجام دهیم-اما در این حالت ، عمدی و برای اهداف اشکال زدایی است.

بیایید با یک پروکسی HTTP حداقل شروع کنیم که می تواند ترافیک رمزگذاری نشده را کنترل کند:

package main

import (
    "fmt"
    "io"
    "net/http"
    "os"
    "os/exec"
    "strings"
    "time"
)

func proxyHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Printf("\n=== REQUEST ===\n")
    fmt.Printf("%s %s\n", r.Method, r.URL.String())

    http.Error(w, "Not implemented", http.StatusNotImplemented)
}

func main() {
    if len(os.Args) < 2 {
        fmt.Println("Usage: httpmon  [args...]")
        os.Exit(1)
    }

    proxyPort := "8080"
    go func() {
        fmt.Printf("Starting proxy on :%s\n", proxyPort)
        http.ListenAndServe(":"+proxyPort, http.HandlerFunc(proxyHandler))
    }()

    time.Sleep(100 * time.Millisecond)

    cmdArgs := os.Args[1:]
    cmd := exec.Command(cmdArgs[0], cmdArgs[1:]...)

    proxyURL := "http://localhost:" + proxyPort
    cmd.Env = append(os.Environ(),
        "HTTP_PROXY="+proxyURL,
        "HTTPS_PROXY="+proxyURL,
    )

    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr
    cmd.Stdin = os.Stdin

    fmt.Printf("Running: %s\n", strings.Join(cmdArgs, " "))
    cmd.Run()
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

این نسخه اساسی:

  • یک سرور پروکسی را در پورت 8080 شروع می کند
  • متغیرهای محیط را برای هدایت ترافیک از طریق پروکسی ما تنظیم می کند
  • دستور مشخص شده را اجرا می کند
  • در حال حاضر فقط درخواست ها را ثبت می کند و خطایی را برمی گرداند

اگر این کار را با آن ایجاد کرده و اجرا کنید ./httpmon curl http://example.com، خواهید دید که این درخواست را ضبط می کند اما هنوز آن را ارسال نمی کند.

ارسال درخواست HTTP

حال بیایید پروکسی ما را در واقع درخواست های HTTP را به جلو بکشیم و هر دو درخواست و پاسخ را وارد کنیم. ما از Go استفاده خواهیم کرد httputil.ReverseProxy برای رسیدگی به وزنه برداری سنگین:

package main

import (
    "bytes"
    "fmt"
    "io"
    "net/http"
    "net/http/httputil"
    "os"
    "os/exec"
    "strings"
    "time"
)

type LoggingTransport struct{}

func (t *LoggingTransport) RoundTrip(req *http.Request) (*http.Response, error) {
    fmt.Printf("\n=== REQUEST ===\n")
    fmt.Printf("%s %s %s\n", req.Method, req.URL.String(), req.Proto)
    fmt.Printf("Host: %s\n", req.Host)
    fmt.Println("\nHeaders:")
    for k, v := range req.Header {
        fmt.Printf("  %s: %s\n", k, strings.Join(v, ", "))
    }

    var bodyBytes []byte
    if req.Body != nil {
        bodyBytes, _ = io.ReadAll(req.Body)
        req.Body = io.NopCloser(bytes.NewReader(bodyBytes))
        if len(bodyBytes) > 0 {
            fmt.Printf("\nBody:\n%s\n", string(bodyBytes))
        }
    }

    transport := &http.Transport{}
    resp, err := transport.RoundTrip(req)
    if err != nil {
        fmt.Printf("\nERROR: %v\n", err)
        return nil, err
    }

    fmt.Printf("\n=== RESPONSE ===\n")
    fmt.Printf("%s %s\n", resp.Proto, resp.Status)
    fmt.Println("\nHeaders:")
    for k, v := range resp.Header {
        fmt.Printf("  %s: %s\n", k, strings.Join(v, ", "))
    }

    respBody, _ := io.ReadAll(resp.Body)
    resp.Body = io.NopCloser(bytes.NewReader(respBody))

    if len(respBody) > 0 {
        fmt.Printf("\nBody:\n%s\n", string(respBody))
    }

    fmt.Println("\n" + strings.Repeat("-", 60))

    return resp, nil
}

func proxyHandler(w http.ResponseWriter, r *http.Request) {
    proxy := &httputil.ReverseProxy{
        Director: func(req *http.Request) {
            if req.URL.Scheme == "" {
                req.URL.Scheme = "http"
            }
            if req.URL.Host == "" {
                req.URL.Host = req.Host
            }
        },
        Transport: &LoggingTransport{},
    }
    proxy.ServeHTTP(w, r)
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

این کد:

  1. ورود به سیستم: حمل و نقل سفارشی که معاملات HTTP را رهگیری و ثبت می کند
  2. پیشرو: از مجاورت واقعی درخواست ها برخورد می کند
  3. درخواست/ورود به سیستم: هدر و بدن را برای هر دو جهت نشان می دهد

در RoundTrip روش جایی است که جادو اتفاق می افتد – برای هر درخواست HTTP فراخوانی می شود و به ما فرصتی می دهد تا همه چیز را قبل و بعد از تماس واقعی شبکه وارد کنیم.

حالا اگر دویدید ./httpmon curl http://httpbin.org/get، هر دو درخواست Curl را مشاهده می کنید و پاسخی که دریافت می کند ، با تمام عناوین و محتوای بدن قابل مشاهده است.

رسیدگی به درخواست های HTTPS

درخواست HTTPS متفاوت کار می کند. هنگامی که مشتری می خواهد درخواست HTTPS را از طریق پروکسی ایجاد کند ، ابتدا درخواست اتصال را برای ایجاد یک تونل ارسال می کند. بیایید پشتیبانی از این را اضافه کنیم:

func handleConnect(w http.ResponseWriter, r *http.Request) {
    fmt.Printf("\n=== CONNECT %s ===\n\n", r.Host)

    targetConn, err := net.Dial("tcp", r.Host)
    if err != nil {
        http.Error(w, err.Error(), http.StatusServiceUnavailable)
        return
    }
    defer targetConn.Close()

    w.WriteHeader(http.StatusOK)

    hijacker, ok := w.(http.Hijacker)
    if !ok {
        http.Error(w, "Hijacking not supported", http.StatusInternalServerError)
        return
    }

    clientConn, _, err := hijacker.Hijack()
    if err != nil {
        http.Error(w, err.Error(), http.StatusServiceUnavailable)
        return
    }
    defer clientConn.Close()

    go io.Copy(targetConn, clientConn)
    go io.Copy(clientConn, targetConn)
}

func proxyHandler(w http.ResponseWriter, r *http.Request) {
    if r.Method == http.MethodConnect {
        handleConnect(w, r)
    } else {

    }
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

این کد:

  1. درخواست های اتصال را تشخیص می دهد (برای HTTPS استفاده می شود)
  2. اتصال TCP را به سرور هدف برقرار می کند
  3. “Hijacks” اتصال HTTP برای دسترسی به TCP خام
  4. یک تونل دو طرفه بین مشتری و سرور ایجاد می کند

با این حال ، با این رویکرد ، ما فقط درخواست اتصال را می بینیم – محتوای واقعی HTTPS رمزگذاری شده است. برای رمزگشایی ترافیک HTTPS ، ما باید خاتمه TLS را انجام دهیم.

تنظیم MITM برای https

برای رمزگشایی ترافیک HTTPS ، ما باید:

  1. مرجع گواهینامه خودمان (CA) را تولید کنید
  2. برای هر میزبان HTTPS گواهینامه هایی ایجاد کنید
  3. خاتمه TLS و رمزگذاری مجدد را انجام دهید

ابتدا بیایید تولید گواهی را اضافه کنیم:

import (
    "crypto/rand"
    "crypto/rsa"
    "crypto/tls"
    "crypto/x509"
    "crypto/x509/pkix"
    "math/big"
)

var (
    caCert *x509.Certificate
    caKey  *rsa.PrivateKey
)

func init() {
    var err error
    caCert, caKey, err = generateCA()
    if err != nil {
        log.Fatal("Failed to generate CA:", err)
    }
}

func generateCA() (*x509.Certificate, *rsa.PrivateKey, error) {
    key, err := rsa.GenerateKey(rand.Reader, 2048)
    if err != nil {
        return nil, nil, err
    }

    template := x509.Certificate{
        SerialNumber: big.NewInt(1),
        Subject: pkix.Name{
            Organization: []string{"HTTP Monitor CA"},
        },
        NotBefore:             time.Now(),
        NotAfter:              time.Now().AddDate(10, 0, 0),
        KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
        ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
        BasicConstraintsValid: true,
        IsCA:                  true,
    }

    certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &key.PublicKey, key)
    if err != nil {
        return nil, nil, err
    }

    cert, err := x509.ParseCertificate(certDER)
    if err != nil {
        return nil, nil, err
    }

    return cert, key, nil
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

این یک CA خود امضا شده را ایجاد می کند که ما برای امضای گواهینامه ها برای هر دامنه HTTPS استفاده خواهیم کرد. CA یک بار با شروع برنامه و در حافظه ذخیره می شود.

رهگیری و رمزگشایی ترافیک HTTPS

حال بیایید رهگیری HTTPS واقعی را پیاده سازی کنیم. ما کنترل کننده اتصال خود را برای انجام خاتمه TLS اصلاح خواهیم کرد:

func generateCert(host string) (*tls.Certificate, error) {
    key, err := rsa.GenerateKey(rand.Reader, 2048)
    if err != nil {
        return nil, err
    }

    template := x509.Certificate{
        SerialNumber: big.NewInt(1),
        Subject: pkix.Name{
            Organization: []string{"HTTP Monitor"},
        },
        NotBefore:    time.Now(),
        NotAfter:     time.Now().AddDate(1, 0, 0),
        KeyUsage:     x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
        ExtKeyUsage:  []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
        DNSNames:     []string{host},
    }

    certDER, err := x509.CreateCertificate(rand.Reader, &template, caCert, &key.PublicKey, caKey)
    if err != nil {
        return nil, err
    }

    cert := &tls.Certificate{
        Certificate: [][]byte{certDER},
        PrivateKey:  key,
    }

    return cert, nil
}

func handleConnect(w http.ResponseWriter, r *http.Request) {
    fmt.Printf("\n=== CONNECT %s ===\n\n", r.Host)

    host, _, err := net.SplitHostPort(r.Host)
    if err != nil {
        host = r.Host
    }

    cert, err := generateCert(host)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

    w.WriteHeader(http.StatusOK)

    hijacker, ok := w.(http.Hijacker)
    if !ok {
        http.Error(w, "Hijacking not supported", http.StatusInternalServerError)
        return
    }

    clientConn, _, err := hijacker.Hijack()
    if err != nil {
        http.Error(w, err.Error(), http.StatusServiceUnavailable)
        return
    }
    defer clientConn.Close()

    tlsConfig := &tls.Config{
        Certificates: []tls.Certificate{*cert},
    }
    tlsConn := tls.Server(clientConn, tlsConfig)
    defer tlsConn.Close()

    reader := bufio.NewReader(tlsConn)

    for {
        req, err := http.ReadRequest(reader)
        if err != nil {
            if err != io.EOF {
                fmt.Printf("Error reading request: %v\n", err)
            }
            break
        }

        req.URL.Scheme = "https"
        req.URL.Host = r.Host
        req.RequestURI = ""

        logRequest(req)

        client := &http.Client{
            Transport: &http.Transport{
                TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
            },
        }

        resp, err := client.Do(req)
        if err != nil {
            fmt.Printf("Error making request: %v\n", err)
            continue
        }

        logResponse(resp)

        resp.Write(tlsConn)
        resp.Body.Close()
    }
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

مفاهیم کلیدی در اینجا:

  1. تولید گواهینامه: ما برای هر دامنه یک گواهی ایجاد می کنیم ، که توسط CA ما امضا شده است
  2. سرور TLS: ما با استفاده از گواهینامه خود یک اتصال TLS با مشتری برقرار می کنیم
  3. حلقه درخواست: ما درخواست های HTTP را از اتصال TLS خوانده ایم ، درخواست های HTTPS واقعی و پاسخ های رو به جلو را انجام می دهیم
  4. مجاورت شفاف: مشتری فکر می کند که با سرور واقعی صحبت می کند

جریان است:

Client → [TLS with our cert] → Our Proxy → [TLS with real cert] → Server
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

این به ما امکان می دهد تا از هر دو جهت ترافیک HTTPS رمزگشایی شده را ببینیم.

اضافه کردن پشتیبانی CURL و AWS CLI

اکنون باید ابزارهایی را برای استفاده از پروکسی خود پیکربندی کنیم و به گواهی CA خود اعتماد کنیم. ابزارهای مختلف به رویکردهای مختلفی نیاز دارند:

func main() {
    if len(os.Args) < 2 {
        fmt.Println("Usage: httpmon  [args...]")
        os.Exit(1)
    }

    caCertPEM := &bytes.Buffer{}
    pem.Encode(caCertPEM, &pem.Block{
        Type:  "CERTIFICATE",
        Bytes: caCert.Raw,
    })

    caCertFile := "/tmp/httpmon-ca.crt"
    err := os.WriteFile(caCertFile, caCertPEM.Bytes(), 0644)
    if err != nil {
        log.Fatal("Failed to write CA cert:", err)
    }

    go func() {
        fmt.Printf("Starting MITM proxy on :%s\n", proxyPort)
        fmt.Printf("CA certificate written to: %s\n", caCertFile)
        http.ListenAndServe(":"+proxyPort, http.HandlerFunc(proxyHandler))
    }()

    time.Sleep(100 * time.Millisecond)

    cmdArgs := os.Args[1:]

    if strings.Contains(cmdArgs[0], "curl") {
        hasProxy := false
        hasCACert := false

        for _, arg := range cmdArgs {
            if arg == "-x" || arg == "--proxy" {
                hasProxy = true
            }
            if arg == "--cacert" {
                hasCACert = true
            }
        }

        newArgs := []string{cmdArgs[0]}
        if !hasProxy {
            newArgs = append(newArgs, "-x", "http://localhost:"+proxyPort)
        }
        if !hasCACert {
            newArgs = append(newArgs, "--cacert", caCertFile)
        }
        newArgs = append(newArgs, cmdArgs[1:]...)
        cmdArgs = newArgs
    }

    cmd := exec.Command(cmdArgs[0], cmdArgs[1:]...)

    proxyURL := "http://localhost:" + proxyPort
    cmd.Env = append(os.Environ(),
        "HTTP_PROXY="+proxyURL,
        "HTTPS_PROXY="+proxyURL,
        "http_proxy="+proxyURL,
        "https_proxy="+proxyURL,
    )

    if strings.Contains(cmdArgs[0], "aws") {
        cmd.Env = append(cmd.Env, "AWS_CA_BUNDLE="+caCertFile)
    }

    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr
    cmd.Stdin = os.Stdin

    fmt.Printf("\nRunning: %s\n", strings.Join(cmdArgs, " "))
    fmt.Println(strings.Repeat("=", 60))

    cmd.Run()
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

این دسته ها:

  1. صادرات گواهینامه CA: گواهی CA ما را ذخیره می کند /tmp/httpmon-ca.crt
  2. پیکربندی فرفری: به طور خودکار اضافه می کند -x (پروکسی) و --cacert پرچم
  3. پیکربندی AWS CLI: مجموعه AWS_CA_BUNDLE متغیر محیط
  4. راه اندازی پروکسی عمومی: متغیرهای محیط استاندارد پروکسی را تنظیم می کند

درک درخواست های AWS S3

وقتی دویدید aws s3 ls bucket-name، این چیزی است که در واقع اتفاق می افتد:

  1. درخواست اتصال: AWS CLI تونل https را به s3.us-east-1.amazonaws.com
  2. ListObjectsv2 تماس API: با پارامترهای خاص درخواست دریافت کنید
  3. احراز هویت: از AWS Signature نسخه 4 با اعتبار خود استفاده می کند

درخواست واقعی به نظر می رسد:

GET /bucket-name?list-type=2&prefix=&delimiter=%2F&encoding-type=url
Host: s3.us-east-1.amazonaws.com
Authorization: AWS4-HMAC-SHA256 Credential=...
X-Amz-Date: 20250513T184908Z
X-Amz-Content-Sha256: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

نمونه

./httpmon aws s3 ls mybucket1
Starting MITM proxy on :8080
CA certificate written to: /tmp/httpmon-ca.crt

Running: aws s3 ls mybucket1
============================================================

=== CONNECT s3.us-east-1.amazonaws.com:443 ===


=== REQUEST #1 ===
Time: 15:06:42
GET https://s3.us-east-1.amazonaws.com:443/mybucket1?list-type=2&prefix=&delimiter=%2F&encoding-type=url HTTP/1.1
Host: s3.us-east-1.amazonaws.com
S3 Bucket: mybucket1

Query Parameters:
  delimiter: /
  encoding-type: url
  list-type: 2
  prefix: 

Headers:
  Accept-Encoding: identity
  User-Agent: aws-cli/2.9.14 Python/3.9.11 Darwin/24.5.0 exe/x86_64 prompt/off command/s3.ls
  X-Amz-Date: 20250513T190642Z
  X-Amz-Content-Sha256: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
  Authorization: AWS4-HMAC-SHA256 Credential=XADSFDSFGDSPZRJWH/20250513/us-east-1/s3/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-date, Signature=0cfd83a5c8dc7b2fe56de3408d23f65ba81a1ad106fd620b6425a7e21c283a7d


=== RESPONSE ===
HTTP/1.1 200 OK

Headers:
  X-Amz-Id-2: w48pC+YLNpgK3Lv9nVP3KDzneks+XXdcwLzQW6SEinz3ggPJCvs7SPdgXV5cfDx9QPGjzUvVfO8=
  X-Amz-Request-Id: CX6A2DCCY1701S6Q
  Date: Tue, 13 May 2025 19:06:43 GMT
  X-Amz-Bucket-Region: us-east-1
  Content-Type: application/xml
  Server: AmazonS3

Body:
version="1.0" encoding="UTF-8"?>
xmlns="http://s3.amazonaws.com/doc/2006-03-01/">mybucket131000/urlfalseindex.html2022-12-23T00:36:07.000Z&quot;5e88427e5bf9dc71b4e1a947ef1c70b3&quot;30STANDARDassetes/images/

------------------------------------------------------------
                           PRE assetes/
                           PRE images/
2022-12-22 19:36:07         30 index.html
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

نکات کلیدی:

  • نام سطل در مسیر URL است ، نه نام میزبان
  • list-type=2 API ListObjectSV2 را مشخص می کند
  • delimiter=/ نتایج گروه ها توسط “پوشه ها”
  • در X-Amz-Content-Sha256 هدر حاوی هش از بدنه درخواست (خالی) است
  • عنوان مجوز شامل امضای AWS است

پاسخ XML حاوی محتوای سطل است ، که AWS CLI سپس در لیست آشنا فرمت می کند.

نمونه فرفری

./httpmon curl https://api.x.com/2/users                                             
Starting MITM proxy on :8080
CA certificate written to: /tmp/httpmon-ca.crt

Running: curl -x http://localhost:8080 --cacert /tmp/httpmon-ca.crt https://api.x.com/2/users
============================================================

=== CONNECT api.x.com:443 ===


=== REQUEST #1 ===
Time: 15:30:11
GET https://api.x.com:443/2/users HTTP/1.1
Host: api.x.com

Headers:
  User-Agent: curl/8.7.1
  Accept: */*


=== RESPONSE ===
HTTP/1.1 401 Unauthorized

Headers:
  Content-Length: 99
  X-Response-Time: 1
  X-Connection-Hash: 2880da004170bfc7797bc51e1d8b94bb27fb00798f03346f480dd37d4a260c41
  Set-Cookie: __cf_bm=kdW6u1gST38Rz8j76Ic.oYGItlWU_3neZwgf8ucpTrU-1747164611-1.0.1.1-k1eAKakMJcMnzW3byJJ9olylAnQXVZv5IfQTvHjmXRnwqsKBcEMr6bcv1cdSoBIW_kgsJrthIaWS5JG_SPPyFBnFYLj.BHQUo9kQA3WOmHU; path=/; expires=Tue, 13-May-25 20:00:11 GMT; domain=.x.com; HttpOnly; Secure; SameSite=None
  Date: Tue, 13 May 2025 19:30:11 GMT
  Perf: 7402827104
  X-Transaction-Id: 8bc4130c588fa097
  Server: cloudflare tsa_b
  Content-Type: application/problem+json
  Strict-Transport-Security: max-age=631138519
  Cf-Cache-Status: DYNAMIC
  Connection: keep-alive
  Cache-Control: no-cache, no-store, max-age=0
  Cf-Ray: 93f491a3bdbfa223-YYZ

Body:
{
  "title": "Unauthorized",
  "type": "about:blank",
  "status": 401,
  "detail": "Unauthorized"
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

کمی بیشتر

بیایید لمس های نهایی را اضافه کنیم تا ابزار ما بهتر شود:

var (
    requestCounter int
    mutex          sync.Mutex
    certCache      = make(map[string]*tls.Certificate)
    certMutex      sync.Mutex
)

func logRequest(req *http.Request) {
    mutex.Lock()
    requestCounter++
    reqID := requestCounter
    mutex.Unlock()

    fmt.Printf("\n\033[36m=== REQUEST #%d ===\033[0m\n", reqID)
    fmt.Printf("Time: %s\n", time.Now().Format("15:04:05"))
    fmt.Printf("%s %s %s\n", req.Method, req.URL.String(), req.Proto)
    fmt.Printf("Host: %s\n", req.Host)

    if strings.Contains(req.Host, ".amazonaws.com") && strings.Contains(req.URL.Path, "https://dev.to/") {
        pathParts := strings.SplitN(req.URL.Path, "https://dev.to/", 3)
        if len(pathParts) >= 2 && pathParts[1] != "" {
            fmt.Printf("\033[93mS3 Bucket: %s\033[0m\n", pathParts[1])
            if len(pathParts) > 2 && pathParts[2] != "" {
                fmt.Printf("\033[93mS3 Key/Prefix: %s\033[0m\n", pathParts[2])
            }
        }
    }

    if req.URL.RawQuery != "" {
        fmt.Println("\nQuery Parameters:")
        params, _ := url.ParseQuery(req.URL.RawQuery)
        for k, v := range params {
            fmt.Printf("  %s: %s\n", k, strings.Join(v, ", "))
        }
    }

    fmt.Println("\nHeaders:")
    for k, v := range req.Header {
        fmt.Printf("  %s: %s\n", k, strings.Join(v, ", "))
    }

    if req.Body != nil {
        body, _ := io.ReadAll(req.Body)
        req.Body = io.NopCloser(bytes.NewReader(body))
        if len(body) > 0 {
            fmt.Printf("\nBody:\n%s\n", string(body))
        }
    }
    fmt.Println()
}

func logResponse(resp *http.Response) {
    fmt.Printf("\n\033[32m=== RESPONSE ===\033[0m\n")
    fmt.Printf("%s %s\n", resp.Proto, resp.Status)

    fmt.Println("\nHeaders:")
    for k, v := range resp.Header {
        fmt.Printf("  %s: %s\n", k, strings.Join(v, ", "))
    }

    if resp.Body != nil {
        body, _ := io.ReadAll(resp.Body)
        resp.Body = io.NopCloser(bytes.NewReader(body))
        if len(body) > 0 {
            fmt.Printf("\nBody:\n")
            if len(body) > 1000 {
                fmt.Printf("%s\n[... %d more bytes ...]\n", string(body[:1000]), len(body)-1000)
            } else {
                fmt.Printf("%s\n", string(body))
            }
        }
    }
    fmt.Println("\n" + strings.Repeat("-", 60))
}

func generateCert(host string) (*tls.Certificate, error) {
    certMutex.Lock()
    if cert, ok := certCache[host]; ok {
        certMutex.Unlock()
        return cert, nil
    }
    certMutex.Unlock()

    key, err := rsa.GenerateKey(rand.Reader, 2048)
    if err != nil {
        return nil, err
    }

    template := x509.Certificate{
        SerialNumber: big.NewInt(1),
        Subject: pkix.Name{
            Organization: []string{"HTTP Monitor"},
        },
        NotBefore:    time.Now(),
        NotAfter:     time.Now().AddDate(1, 0, 0),
        KeyUsage:     x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
        ExtKeyUsage:  []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
        DNSNames:     []string{host, "*." + host},
        IPAddresses:  []net.IP{net.IPv4(127, 0, 0, 1)},
    }

    certDER, err := x509.CreateCertificate(rand.Reader, &template, caCert, &key.PublicKey, caKey)
    if err != nil {
        return nil, err
    }

    cert := &tls.Certificate{
        Certificate: [][]byte{certDER},
        PrivateKey:  key,
    }

    certMutex.Lock()
    certCache[host] = cert
    certMutex.Unlock()

    return cert, nil
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

ما یک رهگیر قدرتمند HTTP/HTTPS را ایجاد کرده ایم که:

  • همه ترافیک HTTP را به طور شفاف ضبط می کند
  • ترافیک HTTPS را با استفاده از تکنیک های MITM رمزگشایی می کند
  • یکپارچه با ابزارهای خط فرمان کار می کند
  • خروجی دقیق و فرمت شده برای اشکال زدایی را ارائه می دهد

این ابزار برای درک تعامل API ، موضوعات اشکال زدایی و یادگیری نحوه برقراری ارتباط ابزارهای مختلف از طریق HTTP بسیار ارزشمند است. کد منبع کامل توانایی های قدرتمند شبکه GO و نحوه ساخت سرورهای پراکسی پیشرفته را نشان می دهد.

به یاد داشته باشید که از این مسئولیت پذیری استفاده کنید – فقط ترافیک را در دستگاه خود برای اهداف اشکال زدایی رهگیری کنید. هرگز از تکنیک های MITM در شبکه ها یا سیستمهایی که متعلق به آنها نیست استفاده نکنید.

شما می توانید اجرای کامل کار این رهگیری http/https را در https://github.com/rezmoss/https-traffic-inspector پیدا کنید

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا