JavaScript实现加密HTTPS请求透传代理:规避自签名证书且防payload泄露
Great question—this is totally feasible, and it’s a smart way to keep your payload end-to-end encrypted while getting around browser restrictions on self-signed certificates. Let’s break down how to pull this off, with concrete code examples for both your Go and Python backends.
Core Concept
What you’re aiming for is end-to-end encrypted request tunneling:
- The browser encrypts the request payload directly using the edge API’s TLS certificate (completing the client-side TLS handshake logic)
- The encrypted raw TLS byte stream is sent to your backend proxy
- Your backend acts as a dumb TCP forwarder—no parsing, no decryption, just passing bytes back and forth between the browser and edge API
- The edge API decrypts the request, processes it, sends back an encrypted response, which your backend forwards to the browser for decryption
This ensures your backend never sees the plaintext payload, exactly what you need.
Step-by-Step Implementation
1. Browser Side: Fetch Edge Certificate & Build Encrypted TLS Requests
Directly constructing TLS packets from scratch is complex, so we’ll use the forge library (a pure-JS crypto toolkit with TLS support) to simplify the process.
First, you’ll need to securely obtain the edge API’s certificate (e.g., hardcode its fingerprint in the frontend and verify against a dynamically fetched cert, or pre-distribute the cert to your frontend):
// Load forge (bundle it for browser use via Webpack/Rollup) import forge from 'node-forge'; // Pre-configured edge API details const EDGE_API_HOST = 'edge-api.yourdomain.com:443'; const EXPECTED_CERT_FINGERPRINT = 'aa:bb:cc:...'; // Hardcode trusted fingerprint // Fetch edge API certificate (or use pre-loaded PEM) async function getEdgeCert() { // In production, fetch this via a secure endpoint or use pre-bundled PEM const certPem = await fetch('/api/edge-cert').then(res => res.text()); return forge.pki.certificateFromPem(certPem); } // Build and send encrypted request to backend proxy async function sendEncryptedRequest(payload) { const edgeCert = await getEdgeCert(); // Verify certificate fingerprint matches trusted value const certFingerprint = forge.pki.getCertificateFingerprint(edgeCert, {md: forge.md.sha256.create()}); if (certFingerprint !== EXPECTED_CERT_FINGERPRINT) { throw new Error('Untrusted edge API certificate'); } // Construct plaintext HTTP request const plaintextRequest = `POST /your/edge/endpoint HTTP/1.1 Host: edge-api.yourdomain.com Content-Type: application/json Content-Length: ${JSON.stringify(payload).length} ${JSON.stringify(payload)}`; // Create TLS client connection const tlsClient = forge.tls.createConnection({ server: false, verify: (conn, verified, depth, certs) => { // We already verified the fingerprint, so bypass default validation return true; }, connected: () => { // Send plaintext request once handshake completes tlsClient.prepare(plaintextRequest); }, tlsDataReady: (encryptedData) => { // Convert forge's binary data to Uint8Array for fetch const byteArray = forge.util.createBuffer(encryptedData).toHex().match(/.{1,2}/g) .map(byte => parseInt(byte, 16)) .reduce((buf, byte) => { buf.push(byte); return buf; }, []); // Send encrypted TLS bytes to backend proxy fetch('/api/proxy', { method: 'POST', body: new Uint8Array(byteArray), headers: { 'X-Edge-Target': EDGE_API_HOST // Tell backend where to forward } }) .then(res => res.arrayBuffer()) .then(responseBuffer => { // Pass edge API's encrypted response back to TLS client for decryption tlsClient.process(forge.util.bytesToHex(new Uint8Array(responseBuffer))); }); }, dataReady: (decryptedResponse) => { // Handle plaintext response from edge API console.log('Edge API response:', decryptedResponse); }, error: (err) => console.error('TLS Error:', err) }); // Initiate TLS handshake tlsClient.handshake(); } // Usage example sendEncryptedRequest({key: "secret-value"});
2. Backend Proxy: Go Implementation
Go is ideal for high-performance TCP forwarding. This code creates a simple HTTP endpoint that forwards raw bytes to the edge API:
package main import ( "io" "net" "net/http" ) func proxyHandler(w http.ResponseWriter, r *http.Request) { // Get target edge API from request header edgeTarget := r.Header.Get("X-Edge-Target") if edgeTarget == "" { http.Error(w, "Missing X-Edge-Target header", http.StatusBadRequest) return } // Establish TCP connection to edge API edgeConn, err := net.Dial("tcp", edgeTarget) if err != nil { http.Error(w, "Failed to connect to edge API", http.StatusServiceUnavailable) return } defer edgeConn.Close() // Set response headers for binary stream w.Header().Set("Content-Type", "application/octet-stream") // Bidirectional data forwarding: browser ↔ edge API go io.Copy(edgeConn, r.Body) io.Copy(w, edgeConn) } func main() { http.HandleFunc("/api/proxy", proxyHandler) http.ListenAndServe(":8080", nil) }
3. Backend Proxy: Python Implementation
For lighter workloads, use Flask with asyncio to handle bidirectional forwarding:
from flask import Flask, request, Response import asyncio app = Flask(__name__) async def forward_data(source_reader, dest_writer): while True: data = await source_reader.read(4096) if not data: break dest_writer.write(data) await dest_writer.drain() dest_writer.close() await dest_writer.wait_closed() async def proxy_tunnel(edge_target, request_data): host, port = edge_target.split(':') port = int(port) # Connect to edge API edge_reader, edge_writer = await asyncio.open_connection(host, port) # Send browser's encrypted data to edge API edge_writer.write(request_data) await edge_writer.drain() # Read encrypted response from edge API response_data = b"" while True: chunk = await edge_reader.read(4096) if not chunk: break response_data += chunk edge_writer.close() await edge_writer.wait_closed() return response_data @app.route('/api/proxy', methods=['POST']) def proxy(): edge_target = request.headers.get('X-Edge-Target') if not edge_target: return "Missing X-Edge-Target header", 400 request_data = request.get_data() response_data = asyncio.run(proxy_tunnel(edge_target, request_data)) return Response(response_data, content_type='application/octet-stream') if __name__ == '__main__': app.run(port=8080, debug=False)
Critical Notes
- Certificate Trust: Always verify the edge API’s certificate fingerprint in the frontend to prevent man-in-the-middle attacks. Never rely on unvalidated certificates.
- TLS Compatibility: Ensure all parties use the same TLS version (TLS 1.3 is recommended for security and performance).
- Performance: The Go implementation will handle high concurrency better than Python. Use Go for production if you expect heavy traffic.
- Error Handling: Add retry logic for TCP connection failures and clear error messages for end users.
内容的提问来源于stack exchange,提问作者gkrizek

