Zum Hauptinhalt springen

Same-Origin-Policy und Web-Proxys: Eine technische Sicherheitsanalyse

Verfasst von
Smith
Smith
Lesezeit
14 Min. Lesezeit
Veröffentlicht am
2. MĂ€rz 2026
Same-Origin-Policy und Web-Proxys: Eine technische Sicherheitsanalyse

Beim Aufbau von ProxyOrb begegnete uns bei jeder Entwurfsentscheidung dasselbe fundamentale Paradox: Ein Web-Proxy funktioniert, indem er den Browser glauben lĂ€sst, dass Inhalte Dritter aus derselben Herkunft stammen. Das bedeutet per Definition, dass das Kernsicherheitsmodell des Browsers ausgetrickst wird. Dabei haben moderne Browser fĂŒnfzehn Jahre damit verbracht, immer ausgefeiltere Abwehrmechanismen genau gegen diese Art von Origin-Verwirrung aufzuschichten.

Dieser Artikel untersucht diese Spannung von Grund auf. Wenn du als Security-Researcher, Penetrationstester oder Browser-Sicherheitsingenieur verstehen möchtest, wie Web-Proxys tatsĂ€chlich mit der Same-Origin-Policy interagieren — nicht auf konzeptueller Ebene, sondern auf Ebene von HTTP-Headern, Chromium-Quellcode und produktiven Engineering-Kompromissen — bist du hier richtig.

Wir behandeln das SOP-Fundament, URL-Rewriting, CORS, CORB/CORP, COEP/COOP, Service Worker, iframes und die Sicherheitsimplikationen fĂŒr Proxy-Betreiber und deren Nutzer.


1. Das Fundament der Same-Origin-Policy

Die Same-Origin-Policy (SOP) wird durch ein Dreiertupel definiert: Schema + Host + Port. Zwei URLs gelten nur dann als gleichrangig (same-origin), wenn alle drei Komponenten exakt ĂŒbereinstimmen. https://example.com:443 und http://example.com:80 sind cross-origin, obwohl sie auf denselben Server zeigen.

Was hÀufig missverstanden wird: was SOP tatsÀchlich verhindert und was es zulÀsst. SOP blockiert nicht:

  • Das Laden von Bildern (<img src>) aus anderen Origins
  • Das Laden von Skripten (<script src>) aus anderen Origins
  • Das Laden von Stylesheets (<link rel="stylesheet">) aus anderen Origins
  • Das Einbetten von iframes aus anderen Origins (auch wenn der Inhalt des eingebetteten Dokuments isoliert ist)
  • Das Absenden von Formularen (<form action>) an andere Origins

Was SOP hingegen verhindert, ist das Lesen der Antwort von Cross-Origin-Anfragen, die ĂŒber fetch() oder XMLHttpRequest gestellt werden. Die Anfrage geht raus — der Netzwerk-Roundtrip findet statt — aber Antwort-Body und Header werden dem JavaScript, das in einer anderen Origin lĂ€uft, vorenthalten.

Diese Unterscheidung ist fĂŒr Web-Proxys enorm wichtig. Die einzige Aufgabe des Proxys besteht darin, zwei Origins (den Proxy-Server und den Zielserver) zu einer einzigen zusammenzufĂŒhren und damit die Cross-Origin-Grenze aufzuheben. Sobald alle Ressourcen scheinbar von https://proxyorb.com kommen, greifen die SOP-basierten BeschrĂ€nkungen beim Lesen von Antworten schlicht nicht mehr.

Der „legitime Raum", den Web-Proxys ausnutzen, ist URL-Rewriting: Statt https://example.com/api/data wird jede Anfrage zu https://proxyorb.com/api/data?__pot=aHR0cHM6Ly9leGFtcGxlLmNvbQ==. Aus Browser-Perspektive ist das eine Same-Origin-Anfrage. Die SOP wurde nicht umgangen — sie wurde durch die Änderung der scheinbaren Herkunft aller Inhalte schlicht gegenstandslos gemacht.


2. ProxyOrbs URL-Rewriting-Architektur

Um die Sicherheitsimplikationen zu verstehen, muss man den Codierungsmechanismus verstehen. ProxyOrb nutzt einen URL-Parameter namens __pot (Proxy Origin Token), der den Base64-kodierten Ziel-Origin enthÀlt.

Der __pot-Parameter

Der Parameter __pot kodiert immer nur den Origin (Schema + Host, ohne Pfad) des Ziels, nicht die vollstÀndige URL. Das bedeutet: https://example.com/some/deep/path?foo=bar und https://example.com/other/page erzeugen denselben __pot-Wert: aHR0cHM6Ly9leGFtcGxlLmNvbQ== (die Base64-Kodierung von https://example.com). Der eigentliche Pfad und die Query-String-Parameter bleiben unverÀndert im Pfad und Query-String der Proxy-URL erhalten.

Wenn ein Nutzer zu https://proxyorb.com/?__pot=aHR0cHM6Ly9leGFtcGxlLmNvbQ== navigiert, dekodiert das Gateway den Wert und rekonstruiert die ursprĂŒngliche URL:

-- Gateway pseudocode: decoding the proxy request

function resolve_original_url(proxy_url):
    pot_value = parse_query_param(proxy_url, "__pot")
    if not pot_value:
        return error("Missing origin token")

    original_origin = base64_decode(pot_value)
    -- e.g. "https://example.com"

    validate_not_private_ip(original_origin)  -- SSRF protection

    -- Replace the proxy host with the target host, keep path/query intact
    original_url = replace_origin(proxy_url, original_origin)
    return original_url

Das Gateway leitet die Anfrage dann an den echten Zielserver weiter und schreibt dabei den Origin-Header um, sodass der Upstream-Server seine eigene Domain sieht und nicht die Proxy-Domain:

-- Set outbound headers toward the target server

request.headers["Host"]   = target_host
request.headers["Origin"] = target_origin   -- e.g. "https://example.com"
-- Remove all internal proxy headers before forwarding

Client-seitiges URL-Rewriting per Service Worker

Das Gateway kĂŒmmert sich um die Serverseite. Die Clientseite — das Umschreiben von URLs in HTML-, JavaScript- und CSS-Antworten, damit alle Sub-Requests weiterhin ĂŒber den Proxy laufen — ĂŒbernimmt ein Service Worker.

Die Kerntransformation wandelt jede im Seiteninhalt gefundene URL in das Proxy-Format um:

-- Service Worker pseudocode: toProxyURL()

function toProxyURL(originalUrl, currentPageUrl):
    if isSameOrigin(originalUrl, currentPageUrl):
        return originalUrl   -- already proxy-origin, no rewrite needed

    -- Extract path/query/hash from the original URL
    -- Build a new URL rooted at the proxy origin
    proxyPath   = extractPathAndQuery(originalUrl)
    potValue    = base64encode(extractOrigin(originalUrl))
    proxyUrl    = proxy_origin + proxyPath + "?__pot=" + potValue

    return proxyUrl

Beispiel: https://cdn.example.com/bundle.js, das auf einer proxied Seite referenziert wird, wird zu https://proxyorb.com/bundle.js?__pot=aHR0cHM6Ly9jZG4uZXhhbXBsZS5jb20=.

Dieses Rewriting ist umfassend: Es deckt fetch(), XMLHttpRequest, <script src>, <img src>, WebSocket-Verbindungen und <link>-Tags ab. Wenn jede URL auf der Seite auf den Proxy-Origin zeigt, stellt der Browser nie eine Cross-Origin-Anfrage — jede Anfrage ist per Konstruktion same-origin.

__pot ohne vollstÀndige Navigation wiederherstellen

Ein subtiler Grenzfall tritt auf bei Anfragen, die innerhalb einer proxied Seite entstehen, aber den __pot-Parameter nicht mitfĂŒhren — zum Beispiel ein relatives fetch('/api/data'), das abgefeuert wird, bevor der Service Worker die URL umgeschrieben hat, oder eine Anfrage aus einem dynamisch injizierten Skript, das den URL-Rewriter umgangen hat.

Der Service Worker löst den fehlenden Token ĂŒber eine kaskadierende Suche auf:

-- Service Worker pseudocode: recovering the origin token

function resolveMissingPot(fetchEvent):
    candidates = [
        getUrlOfControlledTab(fetchEvent.clientId),   -- most reliable
        inferUrlFromFetchEvent(fetchEvent),             -- from clientId / resultingClientId
        fetchEvent.request.referrer,                    -- referrer header
    ]

    for url in candidates:
        pot = extractQueryParam(url, "__pot")
        if pot: return pot

    return null   -- cannot recover; let the gateway handle or reject

Das Gateway verfĂŒgt ĂŒber einen parallelen Wiederherstellungspfad: Wenn eine Anfrage ohne __pot ankommt, aber einen Referer-Header trĂ€gt, der auf eine Same-Origin-URL mit __pot zeigt, extrahiert das Gateway den Token aus dem Referer und hĂ€ngt ihn an die aktuelle Anfrage an. Das deckt Navigationsszenarien ab, bei denen der Token vom JavaScript der Seite entfernt wurde, bevor die Anfrage das Gateway erreichte.


3. CORS: Das explizite Cross-Origin-Berechtigungssystem

CORS (Cross-Origin Resource Sharing) wurde entwickelt, um Servern zu ermöglichen, Cross-Origin-Zugriff durch bestimmte Antwort-Header explizit zu erlauben. Aus Proxy-Perspektive schafft CORS zwei eigenstÀndige Probleme.

Problem 1: CORS-Preflight-Abfangen

Wenn JavaScript auf einer Seite eine fetch()-Anfrage mit nicht-einfachen Headern stellt (z. B. Authorization, Content-Type: application/json), sendet der Browser zunÀchst eine OPTIONS-Preflight-Anfrage. Wenn die Preflight-Antwort des Zielservers keine permissiven CORS-Header enthÀlt, wird die eigentliche Anfrage blockiert.

In ProxyOrbs Architektur fĂ€ngt der Service Worker alle OPTIONS-Anfragen ab und beantwortet sie synthetisch — bevor sie ĂŒberhaupt das Gateway erreichen — und gibt dabei eine Antwort zurĂŒck, die die eigentliche Anfrage freischaltet:

-- Service Worker pseudocode: synthetic CORS preflight

function handlePreflight(request):
    origin = request.headers["Origin"] or "*"

    return Response(status=204, headers={
        "Access-Control-Allow-Origin":      origin,
        "Access-Control-Allow-Methods":     "GET, POST, PUT, DELETE, OPTIONS, PATCH, HEAD",
        "Access-Control-Allow-Headers":     "*",
        "Access-Control-Allow-Credentials": "true",
        "Access-Control-Max-Age":           "86400",
        "Vary":                             "Origin",
    })

Auf Gateway-Ebene erhÀlt jede proxied Antwort gleichwertige CORS-Header:

-- Gateway pseudocode: set CORS on outbound response

response.headers["Access-Control-Allow-Origin"]      = original_origin
response.headers["Access-Control-Allow-Headers"]     = "*"
response.headers["Access-Control-Allow-Credentials"] = "true"

Problem 2: Anfragen mit Anmeldedaten (Credentialed Requests)

Die Kombination aus Access-Control-Allow-Credentials: true und einem nicht-wildcard Access-Control-Allow-Origin ist bedeutsam. CORS verlangt einen expliziten Origin-Wert (nicht *), wenn Anmeldedaten einbezogen werden. Der Service Worker sendet alle ausgehenden Anfragen mit credentials: 'include', daher muss das Gateway den exakten anfragenden Origin zurĂŒckspiegeln und keinen Wildcard-Wert.

Das bedeutet: Alle Cookies, die unter proxyorb.com gespeichert sind — angesammelt von jeder Zielseite, die der Nutzer ĂŒber den Proxy besucht hat — werden bei jeder proxied Anfrage mitgeschickt. Das ist beabsichtigt (es erhĂ€lt den Session-Zustand fĂŒr eingeloggte Seiten), ist aber auch ein wichtiger Sicherheitsaspekt, den wir in Abschnitt 8 besprechen.

Das Header-Strip-and-Replace-Muster bei CORS

Eine proxied Antwort kann CORS-Header vom Zielserver mitbringen, die aus Browser-Perspektive falsch sind (sie referenzieren den Ziel-Origin, nicht den Proxy-Origin). Der Service Worker entfernt und ersetzt sie:

-- Service Worker pseudocode: sanitize response headers

function sanitizeResponseHeaders(response):
    headers = copy(response.headers)

    -- Remove target-site directives that would confuse the browser
    headers.delete("Content-Security-Policy")
    headers.delete("Content-Security-Policy-Report-Only")
    headers.delete("X-Frame-Options")
    headers.delete("X-Content-Type-Options")

    -- Replace with proxy-origin-correct CORS headers
    headers.set("Access-Control-Allow-Origin",      proxy_origin)
    headers.set("Access-Control-Allow-Credentials", "true")
    headers.set("Access-Control-Allow-Headers",     "*")
    headers.set("Vary", "Origin")

    return headers

4. CORB und CORP: Chromiums hÀrtere Abwehrmechanismen

CORS basiert auf explizitem Opt-in durch Server. Chromium hat zwei Mechanismen hinzugefĂŒgt, die standardmĂ€ĂŸig aktiv sind — unabhĂ€ngig von der CORS-Konfiguration.

Cross-Origin Read Blocking (CORB)

CORB wurde in Chrome 67 (2018) als Teil der Spectre-Gegenmaßnahmen eingefĂŒhrt. Die Kernidee: Selbst wenn der Body einer Cross-Origin-Antwort von JavaScript nie gelesen wird, existiert er dadurch, dass er vom Renderer-Prozess abgerufen und dekodiert wurde, im Adressraum dieses Prozesses. Spectre-Angriffe können ihn dann ĂŒber Seitenkanalangriffe extrahieren.

CORB verhindert, dass bestimmte Cross-Origin-Antworten ĂŒberhaupt in den Renderer-Prozess gelangen. Konkret: Wenn ein <script>- oder <img>-Tag eine Cross-Origin-Antwort mit dem Content-Type text/html, text/xml oder application/json abruft, inspiziert Chromium den Body (mit einem MIME-Type-Sniffer, der in der CORB-Spezifikation definiert ist) und ersetzt die Antwort, wenn der Typ ĂŒbereinstimmt, durch eine leere, bevor der Renderer sie je sieht.

FĂŒr einen Web-Proxy wĂ€re CORB katastrophal, wenn Anfragen tatsĂ€chlich Cross-Origin wĂ€ren. Ein API-Endpunkt, der application/json zurĂŒckgibt und von einem <script>-Tag abgerufen wird, wĂŒrde stillschweigend leer zurĂŒckkehren. Da ProxyOrb aber alle URLs als Same-Origin umschreibt, wird CORBs Cross-Origin-Bedingung nie ausgelöst — der Browser sieht nie eine Cross-Origin-JSON-Antwort, denn aus seiner Sicht kommen alle Antworten von proxyorb.com.

Der einzige Fall, in dem CORB relevant wird, ist der Übergangszeitraum, bevor der Service Worker vollstĂ€ndig registriert ist und Anfragen abfĂ€ngt. Wenn eine Anfrage wĂ€hrend dieses Zeitfensters dem URL-Rewriting entkommt, könnte CORB sie blockieren. Diese Race Condition ist der Grund, warum ProxyOrb auch eine Main-Thread-Interceptor-Schicht pflegt, die XMLHttpRequest, fetch und DOM-Attribut-Setter synchron patcht, bevor irgendein Seiten-JavaScript lĂ€uft — als Fallback wĂ€hrend des Service-Worker-Starts.

Es ist erwĂ€hnenswert, dass CORB teilweise durch Opaque Response Blocking (ORB) abgelöst wurde, das Chromium gerade einfĂŒhrt. ORB verfeinert die CORB-Regeln, um False Positives zu reduzieren und gleichzeitig die Spectre-Schutzmaßnahmen beizubehalten. Die Proxy-KompatibilitĂ€tsimplikationen sind Ă€hnlich.

Cross-Origin Resource Policy (CORP)

CORP, definiert in der Fetch-Spezifikation, erlaubt Servern zu deklarieren, dass ihre Ressourcen nur von Same-Origin- oder Same-Site-Kontexten geladen werden dĂŒrfen:

Cross-Origin-Resource-Policy: same-origin

Wenn Chromium diesen Header bei einer Cross-Origin-Sub-Ressource-Anfrage antrifft, blockiert es die Antwort vollstĂ€ndig. Das ist aggressiver als CORB — es gilt unabhĂ€ngig vom Content-Type und lĂ€sst sich nicht durch MIME-Sniffing umgehen.

Da ProxyOrbs URL-Rewriting sicherstellt, dass alle Anfragen aus Browser-Sicht Same-Origin sind, lösen CORP-Header von Zielservern keine Blockierung aus. Das Gateway entfernt diese Header aus Antworten zusĂ€tzlich defensiv, um GrenzfĂ€lle abzudecken, bei denen eine Anfrage ohne URL-Rewriting durchschlĂŒpft.

Das tiefere Problem mit CORP ist eigentlich ein Fall, in dem die Proxy-Architektur die KompatibilitĂ€t verbessert: Wenn https://api.example.com und https://www.example.com beide proxied werden, werden beide auf denselben Proxy-Origin abgebildet — ein Fetch von einer zur anderen ist jetzt echtes Same-Origin. CORP-BeschrĂ€nkungen gelten zwischen ihnen nicht mehr.


5. COEP und COOP: Cross-Origin-Isolation

Ab Chrome 92 (2021) wurde der Zugriff auf SharedArrayBuffer — und damit auf hochauflösende Timer fĂŒr bestimmte Performance-APIs — auf Seiten beschrĂ€nkt, die sich fĂŒr Cross-Origin-Isolation entschieden haben. Dieses Opt-in erfordert zwei zusammenwirkende Antwort-Header.

Cross-Origin-Embedder-Policy (COEP)

Cross-Origin-Embedder-Policy: require-corp

Wenn eine Seite diesen Header sendet, erzwingt der Browser, dass jede von dieser Seite geladene Sub-Ressource entweder:

  1. Same-Origin ist, ODER
  2. explizit Cross-Origin-Resource-Policy: cross-origin sendet, ODER
  3. eine permissive CORS-Antwort fĂŒr den credentialed Fetch aufweist

Das Problem fĂŒr Web-Proxys: Wenn eine Zielseite COEP: require-corp auf ihrem Hauptdokument sendet und dieses Dokument mit dem erhaltenen Header durch den Proxy ausgeliefert wird, verlangt der Browser jetzt, dass auch jede von dieser Seite geladene Sub-Ressource sich einwilligt. Jede Ressource, die das nicht tut — und das sind die meisten — erzeugt einen Netzwerkfehler.

Cross-Origin-Opener-Policy (COOP)

Cross-Origin-Opener-Policy: same-origin

COOP kontrolliert, ob eine Seite eine Browsing-Context-Gruppe mit Cross-Origin-Seiten teilen kann. Wenn auf same-origin gesetzt, öffnet eine Cross-Origin-Navigation eine neue Browsing-Context-Gruppe und unterbricht dabei window.opener-Referenzen und postMessage-KanĂ€le zwischen der Seite und etwaigen Cross-Origin-Öffnern.

FĂŒr einen Proxy ist COOP weniger unmittelbar destruktiv als COEP, bricht aber dennoch Seiten, die auf Cross-Origin-postMessage-Flows angewiesen sind (OAuth-Popup-Flows sind das hĂ€ufigste Beispiel).

Das Dilemma des Proxy-Betreibers

Das ist der schwierigste Kompromiss, dem wir bei ProxyOrb gegenĂŒberstehen:

Option A: COEP und COOP aus Antworten entfernen.

  • Vorteil: Das Laden von Sub-Ressourcen funktioniert normal; die proxied Seite wendet diese BeschrĂ€nkungen nicht an.
  • Nachteil: Wir entfernen Sicherheits-Header, die die Zielseite absichtlich gesetzt hat. Wenn die Seite Cross-Origin-Isolation nutzte, um sensible Daten vor Spectre-Angriffen zu schĂŒtzen, setzt das Entfernen dieser Header dieses Risiko wieder frei.

Option B: COEP und COOP beibehalten.

  • Vorteil: Die Sicherheitsabsicht der Zielseite wird gewahrt.
  • Nachteil: Jede Sub-Ressource, die nicht explizit CORP: cross-origin setzt, kann nicht geladen werden — was die meisten komplexen Web-Anwendungen kaputt macht.

ProxyOrbs aktuelle Strategie ist Option A: Das Gateway entfernt Cross-Origin-Embedder-Policy und Cross-Origin-Opener-Policy aus Antworten. Das maximiert die SeitenkompatibilitĂ€t. Der Sicherheitskompromiss wird bewusst eingegangen: Nutzer eines Web-Proxys akzeptieren, dass sie Inhalte in einer anderen Umgebung als dem ursprĂŒnglich vorgesehenen Deployment-Kontext ausfĂŒhren.

Jeder neue Browser-Sicherheitsmechanismus folgt einem Muster: Er wird als Opt-in eingefĂŒhrt (Seiten können den Header senden, wenn sie Schutz wollen), dann schrittweise zum Standard fĂŒr neue APIs gemacht und schließlich fĂŒr die Pflichtdurchsetzung erwogen. COEP folgte diesem Weg: optional in Chrome 83, erforderlich fĂŒr SharedArrayBuffer in Chrome 91. Seiten, die Drittanbieter-Inhalte ohne Koordination einbetten, stellten fest, dass ihr Inhalt kaputt war. Web-Proxys verschĂ€rfen dieses Problem, weil sie per Definition Drittanbieter-Inhalte vermitteln.

Der Browser-Sicherheits-Community ist dieses Problem bewusst. Der aufkommende Document-Isolation-Policy-Vorschlag zielt darauf ab, Prozess-Isolation von Cross-Origin-Isolation-Anforderungen zu entkoppeln, was es möglicherweise ermöglicht, Spectre-Mitigationen ohne die Opt-in-Pflicht fĂŒr alle Sub-Ressourcen zu erhalten. Wenn das ausgeliefert wird, könnte sich die Proxy-KompatibilitĂ€t mit Isolation-Headern verbessern.


6. Service Worker als browserseitige Vertrauensschicht

Der Service Worker ist nicht bloß eine Optimierung — er ist architektonisch essenziell fĂŒr ProxyOrbs Sicherheitsmodell. Hier ist der Grund.

Registrierungsscope ist an den Origin gebunden

Ein Service Worker wird mit einem scope registriert, der immer innerhalb des Origins der registrierenden Seite liegt. Wenn ProxyOrb seinen Service Worker registriert, ist der Scope https://proxyorb.com/, was bedeutet, er fÀngt jeden Fetch von jeder Seite unter diesem Origin ab.

Das ist der grundlegende Grund, warum URL-Rewriting auf Same-Origin funktioniert: Sobald der SW einen Client kontrolliert, lĂ€uft jede Netzwerkanfrage von diesem Client — egal welche URL das JavaScript auf der Seite zu fetchen versucht — zuerst durch den Service Worker.

// Service Worker entry point
self.addEventListener('fetch', (event) => {
  event.respondWith(handleProxyRequest(event))
})

Die Interceptor-Pipeline

Wenn der Service Worker eine Anfrage abfÀngt, durchlÀuft sie mehrere Entscheidungsstufen:

-- Service Worker pseudocode: request interception pipeline

function handleProxyRequest(fetchEvent):
    request = fetchEvent.request

    -- Stage 1: Synthetic CORS preflight (never hits the network)
    if request.method == "OPTIONS":
        return syntheticCORSResponse(request)

    -- Stage 2: Pass through requests that shouldn't be proxied
    if shouldBypass(request.url):
        return originalFetch(request)

    -- Stage 3: Ensure __pot is present; recover from context if missing
    enrichedRequest = attachOriginToken(request, fetchEvent)

    -- Stage 4: Forward to gateway
    response = originalFetch(enrichedRequest)

    -- Stage 5: Strip and replace security headers in the response
    return sanitizeAndReturn(response)

Transparente Header-Weiterleitung

Eine subtile Herausforderung: Der Browser hindert JavaScript daran, bestimmte „verbotene" Request-Header (Host, Origin, Referer usw.) ĂŒber die Fetch-API zu setzen. Der Service Worker umgeht das, indem er eingeschrĂ€nkte Header in einen einzelnen benutzerdefinierten Passthrough-Header kodiert; das Gateway dekodiert und wendet sie dann an, bevor die Anfrage an den Zielserver weitergeleitet wird:

-- Pseudocode: encode browser-restricted headers for the gateway

function addPassthroughHeaders(request, outboundHeaders):
    restricted = {}
    for name, value in request.headers:
        if name not in COMMON_ALLOWED_HEADERS:
            restricted[name] = value

    if restricted is not empty:
        outboundHeaders["X-Proxy-Passthrough"] = json_encode(restricted)

-- Gateway side: decode and apply before forwarding upstream
function applyPassthroughHeaders(incomingHeaders):
    encoded = incomingHeaders["X-Proxy-Passthrough"]
    if encoded:
        for name, value in json_decode(encoded):
            request.headers[name] = value
        request.headers.delete("X-Proxy-Passthrough")

Sicherheitsimplikationen des SW als vertrauenswĂŒrdiger Vermittler

Aus Sicherheitsperspektive nimmt der Service Worker eine privilegierte Stellung ein: Er kann jede Anfrage aus jeder Seite in seinem Scope inspizieren, modifizieren und fĂ€lschen. FĂŒr legitime Proxy-Nutzung ist das genau der Punkt. FĂŒr einen bösartigen Proxy wĂ€re das eine extrem mĂ€chtige AngriffsflĂ€che.

Deshalb ist die VertrauenswĂŒrdigkeit des Service-Worker-Skripts selbst von höchster Bedeutung. ProxyOrb liefert das Interceptor-Skript von seinem eigenen Origin ĂŒber HTTPS mit strikten Cache-Kontrollen aus. Wenn ein Angreifer einen manipulierten Service Worker einschleusen könnte, könnte er den gesamten Traffic betroffener Nutzer abfangen — nicht nur Proxy-Traffic, sondern jede Anfrage von Seiten unter diesem Origin.


7. iframes: Das Frame-Ancestors-Problem

iframes stellen eine eigenstĂ€ndige Herausforderung gegenĂŒber regulĂ€ren Sub-Ressourcen dar, weil sie einen verschachtelten Browsing-Kontext mit eigener Navigation, eigenener SOP-Grenze und eigenem Satz von EinbettungsbeschrĂ€nkungen umfassen.

X-Frame-Options und frame-ancestors

Zwei Mechanismen verhindern, dass Seiten in iframes eingebettet werden:

Legacy: X-Frame-Options: DENY oder X-Frame-Options: SAMEORIGIN

Modern: Content-Security-Policy: frame-ancestors 'none' oder frame-ancestors 'self'

Wenn das Gateway eine Zielseite proxied, die einen dieser Header sendet, kann die Seite nicht in einem iframe unter dem Proxy-Origin eingebettet werden. Da X-Frame-Options: SAMEORIGIN den Ziel-Origin (example.com) referenziert und der Proxy die Seite von proxyorb.com ausliefert, schlĂ€gt die Same-Origin-PrĂŒfung des Browsers fehl.

Der Proxy muss diese Header aus proxied Antworten entfernen und die CSP durch eine permissive Version ersetzen:

-- Gateway pseudocode: override embedding-restrictive headers

response.headers.delete("X-Frame-Options")
response.headers.delete("Content-Security-Policy")
response.headers.delete("Content-Security-Policy-Report-Only")

-- Set a permissive replacement that allows the proxy to embed content
response.headers["Content-Security-Policy"] =
    "upgrade-insecure-requests; frame-ancestors 'self'; " +
    "default-src * data: blob: about: ws: wss: 'unsafe-inline' 'unsafe-eval'"

iframe-Abfangen und Skript-Injektion

Eingebettete iframes stellen ein zweites Problem dar: Ihr Inhalt wird vom Proxy geladen, aber der Browsing-Kontext des iframes hat nicht automatisch den Service Worker oder den Main-Thread-Interceptor aktiv. Wenn die proxied Seite ein <iframe src="https://widget.example.com/..."> erstellt, muss diese iframe-URL ebenfalls in das Proxy-Format umgeschrieben werden, und die Interceptor-Skripte des Proxys mĂŒssen in das Dokument des iframes injiziert werden.

Der iframe-Interceptor arbeitet auf zwei Ebenen:

Ebene 1 — Prototype-Patching (erfasst programmatische iframe-Erstellung):

-- Pseudocode: intercept iframe src assignment

override HTMLIFrameElement.prototype.src setter:
    if value is not already a proxy URL:
        value = toProxyURL(value)   -- rewrite to proxy format
    call original setter(value)
    scheduleProxyInjection(this)    -- inject interceptor into iframe document

Ebene 2 — MutationObserver-Fallback (erfasst DOM-eingefĂŒgte iframes):

-- Pseudocode: observe DOM for dynamically added iframes

observer = new MutationObserver(mutations):
    for mutation in mutations:
        for node in mutation.addedNodes:
            if node is an <iframe>:
                rewriteSrcIfNeeded(node)
                injectProxyScript(node)

observer.observe(document, { childList: true, subtree: true })

Das sandbox-Attribut-Problem

Das HTML-sandbox-Attribut schrĂ€nkt ein, was ein iframe tun kann: sandbox="allow-scripts allow-same-origin" steuert SkriptausfĂŒhrung, Formularabsendung, Cross-Origin-Zugriff usw. Damit die Proxy-Injektion funktioniert, muss der iframe Skripte ausfĂŒhren und auf den Proxy-Kontext des ĂŒbergeordneten Elements zugreifen können.

Das Token allow-same-origin ist besonders nuanciert: Wenn es zusammen mit allow-scripts vorhanden ist, hebelt es die Origin-Isolation der Sandbox auf — der iframe lĂ€uft mit dem Origin der einbettenden Seite. Wenn es fehlt, erhĂ€lt der iframe einen eindeutigen opaken Origin, was die Proxy-Skript-Injektion vollstĂ€ndig unterbrechen wĂŒrde.

ProxyOrbs aktueller Ansatz fĂŒr Sandbox-Attribute ist, das sandbox-Attribut vollstĂ€ndig zu entfernen, bevor das Proxy-Skript injiziert wird:

-- Pseudocode: handle sandbox attribute before proxy injection

function handleSandboxedIframe(iframe):
    if iframe.hasAttribute("sandbox"):
        -- Remove so the proxy can inject scripts and access contentWindow
        iframe.removeAttribute("sandbox")
    injectProxyScript(iframe)

Das ist ein erheblicher Sicherheitskompromiss: Das Sandboxing wurde von der ursprĂŒnglichen Seite absichtlich gesetzt, um die FĂ€higkeiten des eingebetteten Inhalts einzuschrĂ€nken. Es zu entfernen erweitert, was dieser Inhalt tun kann. Es ist die Art von Entscheidung, die fĂŒr Endnutzer unsichtbar ist, aber die Sicherheitslage der Proxy-Session wesentlich beeinflusst.


8. Sicherheitsimplikationen fĂŒr Proxy-Betreiber

Die AngriffsflĂ€che im Überblick

Wenn Nutzer ĂŒber ProxyOrb surfen, fließen mehrere Kategorien sensibler Daten durch den Origin des Proxys:

Session-Cookies: Da alle Zielseiten ĂŒber proxyorb.com proxied werden, werden Cookies unter diesem Origin gespeichert. Die authentifizierten Sessions eines Nutzers mit seiner Bank, E-Mail und sozialen Medien sind alle Cookies unter einer einzigen Domain. Das credentials: 'include' des Service Workers bedeutet, dass diese Cookies jede proxied Anfrage begleiten.

Referer-Header: Der Referer-Header, der an Upstream-Server gesendet wird, verrĂ€t, welche Seite der Nutzer gerade angeschaut hat. Der Proxy entfernt sorgfĂ€ltig seine eigene Domain aus Referer-Werten, bevor er an den Zielserver weiterleitet. Wenn eine Anfrage von https://proxyorb.com/some/path?__pot=... stammt, wird der ausgehende Referer-Header als die ursprĂŒngliche Ziel-URL rekonstruiert (https://example.com/some/path) — die Proxy-Domain wird nie an Upstream-Server weitergegeben.

-- Pseudocode: normalize referer before forwarding upstream

function sanitizeReferer(referer):
    if referer is a proxy URL:
        return reconstruct_original_url(referer)   -- e.g. "https://example.com/path"
    if referer's origin equals proxy_origin:
        return ""   -- suppress: don't leak proxy domain to upstream
    return referer

JavaScript-AusfĂŒhrungskontext: Jede JavaScript-Datei, die durch den Proxy ausgeliefert wird, wird modifiziert (URLs umgeschrieben) und im Origin des Proxys ausgefĂŒhrt. Ein bösartiges Skript von evil.example.com, das ĂŒber ProxyOrb proxied wird, lĂ€uft im proxyorb.com-Origin und hat vollen Zugriff auf den Local Storage, Cookies und die IndexedDB dieses Origins — was Daten von jeder anderen Zielseite einschließt, auf die der Nutzer ĂŒber den Proxy zugegriffen hat.

Das Bedrohungsmodell bösartiger Proxys

FĂŒr Bildungszwecke lohnt es sich, explizit zu sagen, was ein bösartiger Proxy tun könnte, was ProxyOrb bewusst nicht tut:

  1. Credential Harvesting: Ein bösartiger Proxy könnte jeden Request-Body (der Passwörter und Formulardaten enthÀlt) protokollieren, wÀhrend er das Gateway passiert.

  2. Session Hijacking: Da alle Zielseiten-Cookies unter der Proxy-Domain gespeichert sind, könnte ein bösartiger Proxy-Betreiber serverseitig ĂŒber das Gateway auf diese Cookies zugreifen.

  3. Content Injection: Der Proxy modifiziert notwendigerweise Seiteninhalte (um den Service Worker zu injizieren und URLs umzuschreiben). Ein bösartiger Proxy könnte beliebiges JavaScript injizieren — Keylogger, Krypto-Miner, Ad-Fraud-Skripte — unsichtbar fĂŒr den Nutzer.

  4. SSL Stripping: Wenn der Proxy HTTPS-Verbindungen fĂŒr den Gateway-zu-Nutzer-Abschnitt auf HTTP herunterstuft, ist der gesamte Traffic im Klartext.

ProxyOrb arbeitet durchgehend ĂŒber HTTPS und protokolliert keine Request-Bodies. Der Betreiber eines beliebigen Web-Proxys verfĂŒgt ĂŒber diese FĂ€higkeiten, weshalb die Wahl eines vertrauenswĂŒrdigen Proxy-Betreibers genauso wichtig ist wie die Wahl eines vertrauenswĂŒrdigen VPN-Anbieters.

Mixed Content

Moderne Browser erzwingen Mixed-Content-Blocking: Eine HTTPS-Seite kann keine HTTP-Sub-Ressourcen laden. ProxyOrb behandelt das, indem es HTTPS fĂŒr alle ausgehenden Verbindungen vom Gateway erzwingt, selbst wenn die Ziel-URL HTTP verwendet. Das CSP-Override enthĂ€lt upgrade-insecure-requests, um den Browser anzuweisen, alle HTTP-Sub-Ressourcen-URLs auf HTTPS hochzustufen, bevor sie geladen werden.

Das ist grundsĂ€tzlich wĂŒnschenswert, kann aber Seiten kaputt machen, die Ressourcen absichtlich ĂŒber HTTP ausliefern (Legacy-CDNs, Localhost-Ressourcen usw.).


9. Das anhaltende WettrĂŒsten: Fazit

Als wir begannen, ProxyOrb zu bauen, waren die primÀren KompatibilitÀtsprobleme CORS und X-Frame-Options. Seitdem hat Chromium CORB, CORP, COEP, COOP ausgeliefert und entwickelt Document-Isolation-Policy. Die Reiserichtung ist klar: Browser werden immer aggressiver beim Durchsetzen von Cross-Origin-Grenzen, und jeder neue Mechanismus erfordert, dass die Proxy-Infrastruktur sich anpasst.

Die bedeutendste bevorstehende Herausforderung ist wahrscheinlich die vollstĂ€ndige Verbreitung von COEP in wichtigen Web-Anwendungen. Seiten, die SharedArrayBuffer fĂŒr Leistung nutzen (Video-Editoren, IDEs, Collaboration-Tools), setzen zunehmend COEP: require-corp. Da ProxyOrb diesen Header entfernt, um KompatibilitĂ€t zu erhalten, werden Nutzer, die die Hochleistungsfunktionen benötigen, die COEP ermöglicht, diese in einem Proxy-Kontext eingeschrĂ€nkt finden.

FĂŒr Security-Researcher enthĂŒllt die Proxy-Architektur etwas Wichtiges: Die Same-Origin-Policy ist keine Firewall. Sie ist ein Vertrauensmodell, das auf URL-Struktur basiert. Web-Proxys nutzen aus, dass die SOP keinen intrinsischen Unterschied macht zwischen „diese zwei Ressourcen sind legitim same-origin" und „diese zwei Ressourcen wurden per URL-Rewriting so gemacht, dass sie same-origin aussehen." Den gesamten Browser-Sicherheits-Stack oberhalb der SOP — CORS, CORB, CORP, COEP, COOP — kann man als Versuch betrachten, Abwehrmechanismen hinzuzufĂŒgen, die robuster sind als URL-basierte Origin-IdentitĂ€t.

Das zu verstehen ist essenziell fĂŒr jeden, der Proxy-Dienste auditiert, Browser-Sicherheitsrichtlinien entwirft oder die reale Sicherheitslage von Anwendungen bewertet, auf die möglicherweise ĂŒber Proxys zugegriffen wird.


HĂ€ufig gestellte Fragen

Umgeht ein Web-Proxy die Same-Origin-Policy?

Nicht genau. Ein Web-Proxy arbeitet durch URL-Rewriting: Er wandelt alle Cross-Origin-URLs in Same-Origin-URLs um und macht damit die Cross-Origin-BeschrĂ€nkungen der SOP gegenstandslos, anstatt sie zu umgehen. Aus Browser-Perspektive scheinen alle Inhalte vom eigenen Origin des Proxys zu kommen, sodass keine Cross-Origin-Grenzen ĂŒberschritten werden. Das ist architektonisch verschieden von einem Bypass — die SOP setzt ihre Regeln weiterhin durch; der Proxy engineert den Inhalt nur so, dass diese Regeln nie ausgelöst werden.

Wie wirkt sich CORS auf Web-Proxy-Server aus?

CORS betrifft Proxy-Server auf zwei Ebenen. Erstens muss der Proxy OPTIONS-Preflight-Anfragen abfangen und mit permissiven CORS-Headern antworten, da die Preflight-Antwort des echten Zielservers (die den Ziel-Origin referenziert) die CORS-PrĂŒfung des Browsers fĂŒr den Proxy-Origin nicht bestehen wĂŒrde. Zweitens muss der Proxy CORS-Header aus Zielserver-Antworten entfernen und durch Header ersetzen, die den Proxy-Origin referenzieren. Die Access-Control-Allow-Credentials: true-Einstellung, kombiniert mit credentials: 'include' im Service Worker, bedeutet, dass alle Cookies vom Origin des Proxys jede proxied Anfrage begleiten.

Was ist CORB und welche Auswirkungen hat es auf Proxy-Dienste?

Cross-Origin Read Blocking (CORB) ist eine Chromium-Abwehrmaßnahme, die verhindert, dass bestimmte sensible Cross-Origin-Antworten (HTML, JSON, XML) in den Renderer-Prozess gelangen, wenn sie als Cross-Origin-Sub-Ressourcen geladen werden. FĂŒr korrekt konfigurierte Web-Proxys wird CORB in der Regel nicht ausgelöst, weil das URL-Rewriting alle Anfragen Same-Origin macht. CORB kann jedoch Probleme verursachen im Zeitraum, bevor der Service Worker vollstĂ€ndig registriert ist, wenn einige Anfragen dem URL-Rewriting entkommen und als echte Cross-Origin-Anfragen gesendet werden. CORB wurde als Spectre-Gegenmaßnahme eingefĂŒhrt und ist in der WHATWG-Fetch-Spezifikation definiert.

Können Web-Proxys auf HttpOnly-Cookies zugreifen?

Nein. HttpOnly-Cookies werden vom Zielserver gesetzt und können von JavaScript nicht gelesen werden — auch nicht von JavaScript, das in einem Service Worker lĂ€uft. Das Gateway empfĂ€ngt diese Cookies jedoch beim Weiterleiten von Anfragen im Namen des Nutzers, da der Browser sie in Request-Headern sendet. Das HttpOnly-Flag verhindert den clientseitigen JavaScript-Diebstahl, verhindert aber nicht, dass der Proxy-Server selbst die Cookie-Werte im Transit sieht. Das ist analog dazu, wie HttpOnly gegen XSS schĂŒtzt, aber nicht gegen serverseitige Abfangung.

Ist die Nutzung eines Web-Proxys aus Browser-Sicherheitsperspektive sicher?

Das hĂ€ngt vom Bedrohungsmodell ab. Aus Browser-Perspektive lĂ€uft alles, was ĂŒber einen Web-Proxy ausgeliefert wird, im Origin des Proxys — das bedeutet, eine Schwachstelle im JavaScript einer proxied Seite könnte potenziell auf Daten aus der Session einer anderen proxied Seite zugreifen (da sie denselben Cookie-Jar und Speicher des Origins teilen). Der Betreiber des Proxys ist außerdem eine vertrauenswĂŒrdige Partei mit Zugriff auf den gesamten Traffic im Transit. FĂŒr den Zugriff auf nicht-sensible Inhalte in eingeschrĂ€nkten Umgebungen ist das Risiko typischerweise akzeptabel. FĂŒr authentifizierte Sessions mit sensiblen Diensten (Banking, Gesundheitswesen, Behörden) sollten Nutzer verstehen, dass der Proxy-Betreiber die technische FĂ€higkeit hat, diesen Traffic zu beobachten, und sollten nur Proxy-Dienste mit auditierbaren No-Log-Richtlinien und starken Betriebssicherheitspraktiken verwenden.


Dieser Artikel spiegelt ProxyOrbs Architektur von Anfang 2026 wider. Browser-Sicherheitsmechanismen entwickeln sich schnell weiter; Leser werden ermutigt, den WHATWG-Fetch-Standard, die W3C-Content-Security-Policy-Spezifikation und Chromiums Sicherheits-Design-Dokumente fĂŒr die aktuellsten Informationen zu konsultieren.