Fingerproxy HTTPS Reverse Proxy

repository·master·Indexed 43 days ago

https://github.com/wi1dcard/fingerproxy

An HTTPS reverse proxy that generates JA3, JA4, and Akamai HTTP2 fingerprints, forwarding them to backend services via HTTP headers for bot detection and client identification. It includes a PoC echo-server for fingerprint extraction and the h2i HTTP/2 console debugger.

Tokens
8.7K
Snippets
36
Records
41
Agent score
56%

What's inside Fingerproxy

  1. Quickstart: Run Fingerproxy as a standalone proxy

    master

    To test Fingerproxy in one minute, follow these steps to generate a self-signed certificate, run the binary, and verify the fingerprint headers.

    1. Generate a self-signed certificate for testing:
    openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:secp384r1 -days 3650 \
      -nodes -keyout tls.key -out tls.crt -subj "/CN=localhost" \
      -addext "subjectAltName=DNS:localhost,DNS:*.localhost,IP:127.0.0.1"
    1. Run the Fingerproxy binary (ensure you have downloaded the binary from the releases page):
    ./fingerproxy -listen-addr :8443 -forward-url https://httpbin.org
    1. Verify by sending an HTTPS request using curl:
    curl "https://localhost:8443/anything?show_env=1" --insecure

    Check the response headers for X-Ja3-Fingerprint, X-Ja4-Fingerprint, and X-Http2-Fingerprint.

    # 1. Generate certs
    openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:secp384r1 -days 3650 \
      -nodes -keyout tls.key -out tls.crt -subj "/CN=localhost" \
      -addext "subjectAltName=DNS:localhost,DNS:*.localhost,IP:127.0.0.1"
    
    # 2. Run proxy
    ./fingerproxy -listen-addr :8443 -forward-url https://httpbin.org
    
    # 3. Test
    curl "https://localhost:8443/anything?show_env=1" --insecure
  2. Interact with HTTP/2 frames using h2i

    master

    Once connected to a host, h2i provides an interactive console to debug HTTP/2 traffic. Key capabilities include:

    • Send Raw Frames: You can send specific HTTP/2 frames such as PING or SETTINGS directly.
    • HTTP/1.n to HTTP/2 Translation: You can type standard HTTP/1.n requests (e.g., GET / HTTP/1.1) into the console, and h2i will automatically HPACK and frame them for HTTP/2.
    • Pretty Printing: All received frames from the peer (including HPACK decoding) are displayed in a readable format.
    • Command Completion: The console supports tab completion for commands and options.
    h2i> PING h2iSayHI
    [FrameHeader PING flags=ACK len=8]
      Data = "h2iSayHI"
    
    h2i> headers
    (as HTTP/1.1)> GET / HTTP/1.1
    (as HTTP/1.1)> Host: example.com
    (as HTTP/1.1)>  
  3. Run the JA3 Sorted Extensions Example

    master

    This example demonstrates how to use a JA3 variant that sorts TLS extensions to maintain a consistent fingerprint even when clients like Google Chrome permute the order of extensions in the ClientHello message.

    To run this example, you must first generate self-signed certificates, start the Fingerproxy server with a target forwarding URL, and then verify the headers using curl.

    Steps

    1. Generate certificates: Create tls.crt and tls.key using OpenSSL.
    2. Start the server: Run the Go application, specifying the listen address and the destination URL.
    3. Test the proxy: Use curl to send a request to the local proxy and inspect the returned headers.

    When successful, you will see X-Sorted-Ja3-Fingerprint in the response headers, which remains stable even if the client's extension order changes.

    # 1. Generate fake certificates tls.crt and tls.key
    openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:secp384r1 -days 3650 \
      -nodes -keyout tls.key -out tls.crt -subj "/CN=localhost" \
      -addext "subjectAltName=DNS:localhost,DNS:*.localhost,IP:127.0.0.1"
    
    # 2. TLS server listens on :8443, forwarding requests to httpbin
    go run . -listen-addr :8443 -forward-url https://httpbin.org
    
    # 3. Then test in another terminal
    curl "https://localhost:8443/headers" --insecure
  4. Run the My Fingerprint example

    master

    This example demonstrates how to implement a customized fingerprint using TLS ClientHello. To run the example, you must first generate self-signed certificates for the TLS server, start the fingerproxy server with a listening address and a forwarding URL, and then verify the results using curl.

    # 1. Generate fake certificates tls.crt and tls.key
    openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:secp384r1 -days 3650 \
      -nodes -keyout tls.key -out tls.crt -subj "/CN=localhost" \
      -addext "subjectAltName=DNS:localhost,DNS:*.localhost,IP:127.0.0.1"
    
    # 2. Start the TLS server listening on :8443, forwarding requests to httpbin
    go run . -listen-addr :8443 -forward-url https://httpbin.org
    
    # 3. Test in another terminal
    curl "https://localhost:8443/headers" --insecure
  5. Run the Fingerproxy echo-server

    master

    The echo-server is a Proof of Concept (PoC) utility that demonstrates how to use Fingerproxy as a library. Unlike the main fingerproxy command, it does not forward requests to a backend; instead, it responds directly to the client with the extracted fingerprints.

    Warning: This server is a PoC and does not follow SemVer. APIs may change and it is not tested for production use.

    # 1. Download the binary from GitHub releases
    # 2. Prepare or generate self-signed certificates
    # 3. Run the server
    ./echo-server
    
    # Optional: use flags for log verbosity
    ./echo-server -verbose
    ./echo-server -quiet
  6. Use Fingerproxy as a Go library

    master

    Fingerproxy can be integrated directly into your Go projects as a library. For detailed API documentation, including function signatures and types, refer to the official Go documentation on pkg.go.dev.

    Note on Vendored Packages: If you need to use the specialized implementations of http2 or ja3 provided in this repository, you should import them from their original source locations within the project to ensure compatibility with Fingerproxy's internal logic.

    https://pkg.go.dev/github.com/wi1dcard/fingerproxy/pkg
  7. Configure Kubernetes Liveness Probes and Prometheus Metrics

    master

    Fingerproxy includes built-in support for Kubernetes and observability.

    Kubernetes Liveness Probes

    Fingerproxy supports Kubernetes liveness probes via HTTPS. To prevent probes from being forwarded to your backend, Fingerproxy recognizes the kube-probe/1.26 (and similar) User-Agent. When a probe is detected, Fingerproxy responds with an HTTP 200 directly without forwarding the request.

    Example Pod Spec:

    apiVersion: v1
    kind: Pod
    metadata:
      name: fingerproxy
    spec:
      containers:
      - name: fingerproxy
        image: fingerproxy
        livenessProbe:
          httpGet:
            path: /
            port: 443
            scheme: https

    Prometheus Metrics

    The Prometheus metrics server listens by default on port :9035. You can view available metrics by querying the endpoint:

    curl http://localhost:9035/
  8. How to set trailers in HTTP/2 responses

    master

    In HTTP/2, trailers are sent as a separate frame at the end of the stream. While the standard Go http.ResponseWriter mechanism requires trailers to be pre-declared in the Header() map, this implementation provides a more flexible way to handle them using a magic prefix.

    Using the TrailerPrefix: If you want to set a trailer that wasn't known when the headers were first sent (common in gRPC or streaming scenarios), add the key to the Header() map using the Trailer: prefix. The prefix is stripped, and the value is sent as a trailer after the handler finishes.

    Example:

    // This will be sent as a trailer, not a response header
    w.Header().Set("Trailer:X-Custom-Trace-ID", "12345")
    const TrailerPrefix = "Trailer:"
  9. Handle stream timeouts with onReadTimeout and onWriteTimeout

    master

    The server manages stream-level timeouts via two primary mechanisms:

    • Read Timeout: onReadTimeout is triggered when a stream's read deadline expires. It closes the request body with os.ErrDeadlineExceeded.
    • Write Timeout: onWriteTimeout is triggered when a stream's write deadline expires. It sends an RST_STREAM frame with ErrCodeInternal and os.ErrDeadlineExceeded to the peer.
  10. Customize fingerprinting algorithms via Header Injectors

    master

    Fingerproxy uses HeaderInjector functions to add fingerprint data (like JA3, JA4, or HTTP2 fingerprints) to requests before they are forwarded to the backend.

    By default, GetHeaderInjectors returns DefaultHeaderInjectors, which injects:

    • X-JA3-Fingerprint (JA3 fingerprint)
    • X-JA4-Fingerprint (JA4 fingerprint)
    • X-HTTP2-Fingerprint (HTTP2 fingerprint)

    To use your own fingerprinting logic, you must override the GetHeaderInjectors variable with a function that returns a slice of reverseproxy.HeaderInjector.

    // Example: Overriding the default injectors to use custom headers
    fingerproxy.GetHeaderInjectors = func() []reverseproxy.HeaderInjector {
        return []reverseproxy.HeaderInjector{
            fp.NewFingerprintHeaderInjector("X-My-Custom-Fingerprint", fp.JA3Fingerprint),
        }
    }
  11. Run the JA3 Raw Fingerprint Example

    master

    This example demonstrates how to configure Fingerproxy to pass the raw JA3 fingerprint (the unhashed version) to the backend service. This is useful when you need the full details of the TLS handshake parameters rather than just the MD5 hash.

    To run this example, you must:

    1. Generate local TLS certificates for the proxy to use.
    2. Start the Fingerproxy server, specifying a listening address and a target backend URL.
    3. Send a request to the proxy to verify the headers.
    # 1. Generate fake certificates tls.crt and tls.key
    openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:secp384r1 -days 3650 \
      -nodes -keyout tls.key -out tls.crt -subj "/CN=localhost" \
      -addext "subjectAltName=DNS:localhost,DNS:*.localhost,IP:127.0.0.1"
    
    # 2. TLS server listens on :8443, forwarding requests to httpbin
    go run . -listen-addr :8443 -forward-url https://httpbin.org
    
    # 3. Test in another terminal
    curl "https://localhost:8443/headers" --insecure