Integrasi reverse-proxy web-to-API mentransformasikan antarmuka web interaktif internal (seperti Google Gemini web UI) menjadi endpoint API terstruktur (seperti OpenAI-compatible /v1/chat/completions). Pola ini sering digunakan untuk menghemat biaya token API resmi, namun membawa konsekuensi teknis besar terkait stabilitas upstream, pemblokiran bot, dan kerentanan keamanan sesi.
Arsitektur Translasi Payload Web Internal
Layanan berbasis web mengandalkan endpoint internal yang diproteksi sesi browser (RPC berbasis Protobuf over HTTP, gRPC-web, atau payload JSON proprietary tak terdokumentasi). Sebaliknya, standard tooling AI mengasumsikan REST payload OpenAI standar.
Adapter web-to-API bekerja dengan tiga komponen inti:
- Schema Ingress Adapter: Menerima payload standar (JSON schema OpenAI: model, messages, temperature).
- Session Context Manager: Menginjeksi cookie autentikasi browser aktif (misalnya
__Secure-1PSID) dan token anti-CSRF (seperti parameteratpada Google Batchexecute). - Stream Egress Parser: Mengurai respons streaming web (seringkali format chunked stream, SSE internal, atau newline-delimited JSON) kembali ke chunk Server-Sent Events (SSE) standar OpenAI (
data: {...}).
Risiko Keamanan dan Kerapuhan Operasional
1. Session Hijack dan Token Leak
Untuk mengautentikasi ke UI internal, proxy membutuhkan cookie sesi pengguna riil. Session token ini membawa privileges penuh atas akun induk pemilik sesi. Jika proxy web-to-API diekspos tanpa isolasi ketat atau logs masking, kebocoran satu token mengizinkan pengambilalihan akun secara penuh (session hijack), bukan sekadar penyalahgunaan API quota.
2. Bot Detection Bypass & Auth Rotation
Penyedia web UI aktif mendeteksi automation melalui fingerprinting TLS/JA3, IP reputation scoring, dan tantangan bot dinamis (misalnya Cloudflare Turnstile atau reCAPTCHA). Auth token web memiliki masa kedaluwarsa pendek dan invalidasi otomatis saat IP berubah. Penggunaan statis cepat memicu HTTP 403 Forbidden atau silent captchas.
3. Ketiadaan Service Level Agreement (SLA)
Endpoint web internal dapat berubah tanpa pemberitahuan rilis. Perubahan nama field internal, modifikasi hashing token CSRF, atau transisi payload batchexecute akan langsung memutus adapter produksi.
Implementasi Adapter Proxy dan Error Translation
Implementasi adapter harus menerjemahkan HTTP status code internal ke standar REST error. Bila upstream mendeteksi bot atau session kedaluwarsa (403) atau membatasi request (429), adapter wajib memetakan status tersebut tanpa membocorkan cookie sensitif di payload error.
import time
from collections import deque
from fastapi import FastAPI, HTTPException, Request, Response, status
import httpx
app = FastAPI()
# ponytail: Hardcoded session config. Move to Vault/KMS in production.
UPSTREAM_URL = "https://gemini.google.com/_/BardChatUi/data/batchexecute"
SESSION_COOKIE = "__Secure-1PSID=fake_session_token_xyz;"
class SlidingWindowLimiter:
def __init__(self, limit: int, window_sec: float):
self.limit = limit
self.window = window_sec
self.requests = deque()
def allow(self) -> bool:
now = time.time()
while self.requests and self.requests[0] <= now - self.window:
self.requests.popleft()
if len(self.requests) < self.limit:
self.requests.append(now)
return True
return False
# Batasi maksimal 10 request per 60 detik lokal untuk menghindari flag upstream
limiter = SlidingWindowLimiter(limit=10, window_sec=60.0)
@app.post("/v1/chat/completions")
async def chat_completions(req: Request):
if not limiter.allow():
raise HTTPException(
status_code=status.HTTP_429_TOO_MANY_REQUESTS,
detail={"error": {"message": "Rate limit proxy lokal tercapai. Cegah upstream ban.", "type": "rate_limit_error"}}
)
body = await req.json()
messages = body.get("messages", [])
last_prompt = messages[-1]["content"] if messages else ""
# Translasi payload internal (simulasi format RPC array)
internal_payload = f"f.req=[null,\"[[\\\"{last_prompt}\\\"]]]\"]"
headers = {
"Cookie": SESSION_COOKIE,
"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36"
}
async with httpx.AsyncClient(timeout=30.0) as client:
try:
upstream_res = await client.post(UPSTREAM_URL, content=internal_payload, headers=headers)
except httpx.RequestError as exc:
raise HTTPException(status_code=502, detail=f"Upstream unreachable: {str(exc)}")
# Error translation: downstream caller tidak boleh menerima raw HTML/cookie
if upstream_res.status_code == 429:
raise HTTPException(status_code=429, detail="Upstream 429: Web bot rate limit tercapai.")
if upstream_res.status_code in (401, 403):
raise HTTPException(status_code=502, detail="Upstream session invalidated or challenge triggered.")
if upstream_res.is_error:
raise HTTPException(status_code=upstream_res.status_code, detail="Upstream internal error.")
# Simulasi parsing raw payload internal menjadi respons OpenAI standar
# response ponytail: parsing dummy untuk struktur payload output minimum
return {
"id": "chatcmpl-mock",
"object": "chat.completion",
"created": int(time.time()),
"model": body.get("model", "web-gemini"),
"choices": [{
"index": 0,
"message": {"role": "assistant", "content": "Parsed content from upstream response"},
"finish_reason": "stop"
}]
}
Mitigasi Abuse dan Rate Limit Upstream
Endpoint web upstream menerapkan algoritma rate limit yang jauh lebih agresif dibanding API berbayar resmi. Ketika kuota web terlampaui, pemblokiran akun dapat bersifat permanen.
- Local Rate Limiting (Sliding Window): Seperti pada implementasi
SlidingWindowLimiterdi atas, pembatasan lokal wajib dipasang di layer proxy sebelum request diteruskan ke upstream. Tujuannya adalah meratakan burst request. - Queueing dan Concurrency Locks: Karena satu sesi web browser didesain untuk satu interaksi manusia, proses multi-thread dengan satu token akan memicu suspensi. Pasang
asyncio.Semaphore(1)per sesi autentikasi upstream. - Credential Decoupling: Jangan pernah membagikan token web upstream langsung ke client. Proxy harus bertindak sebagai trust boundary mutlak yang memvalidasi API key pengguna lokal sebelum menyuntikkan cookie internal.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!