Zum Hauptinhalt springenProxyOrb

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

Verfasst von
Smith
Smith
Lesezeit
14 Min. Lesezeit
Veröffentlicht am
Mar 2, 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.

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