Einführung
Eine Upload-Seite im Browser kann fehlschlagen, obwohl eine presigned URL mit curl funktioniert. Der Browser prüft zusätzlich, ob der Objektspeicherdienst den Origin der Seite erlaubt. Sie konfigurieren diese unabhängige CORS-Grenze für eine bereitgestellte synthetische Upload-Seite und halten den Bucket dabei privat.
Schließen Sie zuerst „Authorize Temporary File Access“ ab. Diese neue VM stellt Node.js 22.22.0, Wrangler 4.131.1 und AWS SDK 3.888.0 in festgelegten Versionen bereit. Außerdem enthält sie ein vollständiges, für dieses Labor nicht relevantes Grundgerüst einer Upload-Seite. Sie erstellen einen neuen privaten Bucket, eine kurzlebige Objektberechtigung und eine kurzlebige Worker-Seite. R2 muss bereits aktiviert sein. Lesen Sie die Informationen zu R2 pricing und zum CORS-Verhalten. Eine gekaufte Domain ist nicht erforderlich. Verwenden Sie ausschließlich den bereitgestellten synthetischen Text. Bereinigen Sie alle Ressourcen und widerrufen Sie die Labortokens.
Den Anwendungs-Bucket verbinden
In diesem Schritt autorisieren Sie diese VM und erstellen einen unabhängigen privaten Bucket für die Anwendung. Die Geräteautorisierung bestätigt Ihr Lernkonto. Für die Verwaltung des R2-Buckets verwenden Sie ein separates API-Token, das auf dieses Konto beschränkt ist.
Starten Sie Bash für die unten verwendete Befehlssyntax. Wechseln Sie anschließend in das vorbereitete Projekt und prüfen Sie die Werkzeuge. Lassen Sie dasselbe Terminal geöffnet, damit die Variablen mit den Ressourcennamen erhalten bleiben:
bash
cd /home/labex/project/r2-lab
export PATH="$PWD/.tools/node-v22.22.0-linux-x64/bin:$PATH"
node --version
npx wrangler --version
Autorisieren Sie den angezeigten Gerätecode in Ihrem eigenen Browser. Prüfen Sie das Lernkonto sowie die angeforderten Lese-Bereiche für Konto und Benutzer, bevor Sie Ihre Zustimmung erteilen:
npx wrangler login --device --browser=false --scopes account:read user:read workers_scripts:write workers_kv:write
npx wrangler whoami --json
Verlangen Sie loggedIn: true. Lesen Sie den Kontonamen auch dann ab, wenn nur ein Konto aufgeführt ist. Ersetzen Sie YOUR_ACCOUNT_ID unten durch die tatsächliche 32-stellige ID dieses Kontos. openssl rand -hex 6 erzeugt zwölf zufällige hexadezimale Zeichen, damit dieses Labor nicht mit einem früheren Durchlauf kollidiert. Das Here-Dokument schreibt eine standardmäßige Konfigurationsdatei; die Shell setzt dabei Ihre Variablen ein.
ACCOUNT_ID=YOUR_ACCOUNT_ID
RUN_ID=$(openssl rand -hex 6)
NAME="labex-c05-r05-$RUN_ID"
BUCKET="$NAME-docs"
cat > wrangler.jsonc <<JSON
{"name":"$NAME","account_id":"$ACCOUNT_ID","main":"src/index.js","workers_dev":true,"compatibility_date":"2026-07-30","r2_buckets":[{"binding":"DOCUMENTS","bucket_name":"$BUCKET"}]}
JSON
Öffnen Sie für die Bucket-Verwaltung in Ihrem Cloudflare-Profil die Seite API Tokens und erstellen Sie ein benutzerdefiniertes Token mit einem Namen, der sich auf dieses Labor bezieht. Gewähren Sie Account → Workers R2 Storage → Edit und beschränken Sie Account Resources auf das Lernkonto, dessen ID Sie gespeichert haben. Legen Sie einen kurzen Ablaufzeitraum fest. Beziehen Sie keine anderen Konten oder nicht benötigten Berechtigungen ein. Diese Berechtigung auf Kontoebene kann Buckets erstellen und löschen; das objektbezogene Token im nächsten Schritt kann das nicht.
Kopieren Sie das Token genau einmal in die versteckte Eingabeaufforderung dieser VM. umask 077 beschränkt den Dateizugriff auf Ihren Benutzer; read -s blendet die Eingabe aus. Die Datei verwendet Wranglers standardmäßige Token-Variable und ist von Git ausgeschlossen.
umask 077
read -r -s -p 'R2 management API token: ' R2_MANAGEMENT_TOKEN; printf '\n'
printf 'CLOUDFLARE_API_TOKEN=%s\n' "$R2_MANAGEMENT_TOKEN" > .env.management
unset R2_MANAGEMENT_TOKEN
Verwenden Sie --env-file=.env.management nur für R2-Verwaltungsbefehle. Der normale Befehl whoami prüft weiterhin die Geräteautorisierung der VM.
Setzen Sie --env-file an das Ende jedes Wrangler-Befehls, damit dessen Liste von Dateiargumenten nicht auch den Befehlsnamen erfasst. Wenn Wrangler nach dem Erstellen eines Buckets anbietet, eine Bindung zur Konfiguration hinzuzufügen, geben Sie n ein und drücken Sie die Eingabetaste. Die vorgesehene Bindung ist bereits konfiguriert.
npx wrangler r2 bucket create "$BUCKET" --env-file=.env.management
Listen Sie Ihre Buckets auf und suchen Sie den exakt generierten Namen. Andere Buckets gehören zu anderen Arbeiten; lassen Sie sie unverändert.
npx wrangler r2 bucket list --env-file=.env.management
Öffnen Sie im Dashboard Storage & databases → R2 → Overview, wählen Sie genau diesen Bucket aus und prüfen Sie die leere Objektliste. Lassen Sie in den Einstellungen die öffentliche Entwicklungs-URL und benutzerdefinierte Domains deaktiviert. Ein Bucket-Name im Dashboard bestätigt die Identität des Buckets; spätere Download-Prüfungen bestätigen die gespeicherten Bytes.
Die Berechtigung für Worker-Skripte ermöglicht die Bereitstellung. Die KV-Berechtigung unterstützt Wranglers Löschverwaltung; dieses Labor erstellt keinen KV-Namespace. Das R2-Verwaltungstoken bleibt eine separate, auf das Konto beschränkte Berechtigung.
Die Upload-Seite und den Signaturzugriff vorbereiten
In diesem Schritt stellen Sie die bereitgestellte Seite bereit und geben dem Terminal eine auf diesen Bucket beschränkte Berechtigung. Die Seite enthält ausschließlich eine feste synthetische Nutzlast und ein leeres URL-Feld. Das langfristig gültige Signaturgeheimnis gelangt niemals auf die Seite.
Die S3-kompatible API ermöglicht Standard-Speicher-SDKs den Zugriff auf R2. Sie verwendet ein separates Zugriffsschlüsselpaar statt des Geräte-Tokens von Wrangler. Öffnen Sie in R2 Overview den Bereich Account Details → API Tokens → Manage und erstellen Sie anschließend ein User API token mit einem Namen, der sich auf den generierten Ressourcennamen dieses Labors bezieht. Wählen Sie Object Read & Write, beschränken Sie das Token auf genau diesen neuen Bucket und wählen Sie einen kurzen Ablaufzeitraum, falls das Formular diese Option anbietet. Wählen Sie weder alle Buckets noch den Administratorzugriff. Lassen Sie diese Token-Seite geöffnet, bis Sie das einmalige Geheimnis gespeichert haben.
Verwenden Sie die folgenden Bash-Eingabeaufforderungen in der VM. read -s blendet die Eingabe aus; umask 077 sorgt dafür, dass nur Ihr Benutzer die Berechtigungsdatei lesen kann. Diese Namen sind die standardmäßigen Umgebungsvariablen des AWS SDK. Fügen Sie die Access Key ID und den Secret Access Key in die jeweiligen Eingabeaufforderungen ein und drücken Sie anschließend die Eingabetaste. Fügen Sie nicht den Wert des allgemeinen API-Tokens ein.
umask 077
read -r -s -p 'Access Key ID: ' AWS_ACCESS_KEY_ID; printf '\n'
read -r -s -p 'Secret Access Key: ' AWS_SECRET_ACCESS_KEY; printf '\n'
printf 'AWS_ACCESS_KEY_ID=%s\nAWS_SECRET_ACCESS_KEY=%s\n' "$AWS_ACCESS_KEY_ID" "$AWS_SECRET_ACCESS_KEY" > .env.s3
unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY
Erstellen Sie einen wiederverwendbaren Standard-SDK-Client. Das SDK benötigt eine Regionszeichenfolge; für R2 verwenden Sie auto. Durch das Einlesen der vorhandenen Konfiguration richten CLI- und SDK-Vorgänge dasselbe Konto und denselben Bucket ein.
cat > storage.mjs <<'JS'
import { S3Client } from "@aws-sdk/client-s3";
import { readFileSync } from "node:fs";
const config = JSON.parse(readFileSync("wrangler.jsonc", "utf8"));
export const Bucket = config.r2_buckets[0].bucket_name;
export const s3 = new S3Client({
region: "auto",
requestChecksumCalculation: "WHEN_REQUIRED",
endpoint: `https://${config.account_id}.r2.cloudflarestorage.com`,
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY
}
});
JS
requestChecksumCalculation: "WHEN_REQUIRED" verhindert, dass vor der Nutzlast des Browsers eine Prüfsumme für einen leeren Inhalt signiert wird. Die URL signiert weiterhin Objekt und Operation; der folgende Signaturaufruf schließt Content-Type ausdrücklich ein. Die abschließende Prüfung vergleicht die gespeicherten Bytes.
Veröffentlichen Sie die bereitgestellte Seite:
npx wrangler deploy
Übernehmen Sie die HTTPS-Bereitstellungs-URL ohne Pfad und ohne abschließenden Schrägstrich in ORIGIN. Ein Origin besteht aus Schema, Hostname und Port; https://example.com unterscheidet sich von http://example.com. Speichern Sie den Origin für die spätere unabhängige Prüfung:
ORIGIN=https://YOUR_WORKER.YOUR_SUBDOMAIN.workers.dev
printf "%s\n" "$ORIGIN" > origin.txt
Öffnen Sie diese URL in Ihrem Browser. Die Seite Synthetic export upload muss mit einem leeren Feld für die temporäre URL angezeigt werden. Laden Sie noch nichts hoch; der Bucket erlaubt diesen Browser-Origin bisher nicht.
Einen Browser-Origin erlauben
In diesem Schritt konfigurieren Sie Cross-Origin Resource Sharing (CORS). Bevor ein Browser einen Cross-Origin-PUT sendet, fragt er den Bucket, ob der Origin, die Methode und die Header dieser Seite zulässig sind. Diese OPTIONS-Anfrage ist ein Preflight. CORS steuert den Browserzugriff; es ersetzt nicht die Autorisierung des signierten Speichervorgangs.
Schreiben Sie eine Policy mit dem tatsächlichen Origin der Seite. Das Terminal setzt $ORIGIN in das JSON ein. Wrangler verwendet das kleingeschriebene Format rules/allowed von Cloudflare statt des AWS-Formats CORSRules:
cat > cors.json <<JSON
{"rules":[{"allowed":{"origins":["$ORIGIN"],"methods":["PUT"],"headers":["content-type"]},"exposeHeaders":["ETag"],"maxAgeSeconds":60}]}
JSON
Die bereitgestellte Seite benötigt nur PUT und Content-Type. exposeHeaders erlaubt JavaScript, das zurückgegebene ETag zu lesen. Der kurze Wert für maxAgeSeconds reduziert während des Tests die Dauer des Preflight-Cachings.
npx wrangler r2 bucket cors set "$BUCKET" --file cors.json --env-file=.env.management
npx wrangler r2 bucket cors list "$BUCKET" --env-file=.env.management
Bestätigen Sie, dass die Policy ausschließlich für diesen neu erstellten Bucket überschrieben wird. Die resultierende Liste muss Ihren exakten Origin anzeigen. Prüfen Sie die CORS-Policy in den Dashboard-Einstellungen des Buckets nur lesend und lassen Sie den öffentlichen Zugriff deaktiviert.

Beispiel: Die Richtlinie erlaubt diesen Ursprung, PUT und content-type. Ihr erzeugter Hostname wird abweichen. Die CLI-Ausgabe prüft außerdem den freigegebenen ETag und die Cache-Dauer.
Über den Browser hochladen
In diesem Schritt kombinieren Sie die Berechtigung für den signierten PUT mit der Origin-Policy des Browsers. Der bereits erläuterte Signierer erstellt eine URL genau für uploads/browser.txt; die Seite sendet denselben Content-Type, der beim Signieren verwendet wurde.
cat > sign-upload.mjs <<'JS'
import { PutObjectCommand } from "@aws-sdk/client-s3";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
import { writeFileSync } from "node:fs";
import { s3, Bucket } from "./storage.mjs";
const url = await getSignedUrl(s3, new PutObjectCommand({
Bucket, Key: "uploads/browser.txt", ContentType: "text/plain"
}), { expiresIn: 300, signableHeaders: new Set(["content-type"]) });
writeFileSync("put-url.txt", url, { mode: 0o600 });
console.log("Prepared a five-minute PUT URL");
JS
node --env-file=.env.s3 sign-upload.mjs
Zeigen Sie diesen temporären Link nur so lange an, wie Sie ihn in die bereitgestellte Seite kopieren. Geben Sie ihn nicht weiter, nehmen Sie ihn nicht in Screenshots auf und fügen Sie stattdessen nicht das S3-Geheimnis ein:
cat put-url.txt
Fügen Sie den Link in Temporary PUT URL ein und wählen Sie innerhalb von fünf Minuten Upload synthetic text. Es muss ein erfolgreicher HTTP-Status im Bereich 2xx und ein sichtbares ETag angezeigt werden. Leeren Sie das URL-Feld nach dem Upload. Eine abgelaufene URL kann als allgemeiner Browser-CORS-Fehler erscheinen, weil Fehlerantworten möglicherweise keine CORS-Header enthalten. Erzeugen Sie bei Bedarf einen neuen Link.
Laden Sie das tatsächliche Objekt mit Wrangler herunter und vergleichen Sie die feste Nutzlast:
npx wrangler r2 object get "$BUCKET/uploads/browser.txt" --remote --file browser-download.txt --env-file=.env.management
printf "Synthetic browser export.\n" > expected-browser.txt
cmp expected-browser.txt browser-download.txt
Die Plattformprüfung liest die Bytes und den Content-Type unabhängig. Die tatsächliche Browserinteraktion bleibt für dieses Labor erforderlich; curl allein erzwingt keine Browser-CORS-Regeln.

Der echte Browser lieferte HTTP 200 und einen lesbaren ETag; das Feld für die temporäre URL wurde geleert. Die Seite zeigt \n wörtlich an, lädt aber einen echten Zeilenumbruch hoch. Der unabhängige Download bestätigt den exakten Inhalt von 26 Bytes.
Origin-Policy und Signaturberechtigung trennen
In diesem Schritt prüfen Sie einen erlaubten und einen nicht erlaubten Preflight, ohne ein weiteres Objekt zu erstellen. Diese curl-Anfragen fragen ab, was der Browser ausführen darf; curl selbst setzt diese Antwort nicht durch.
ACCOUNT_ID=$(node -p "JSON.parse(require('fs').readFileSync('wrangler.jsonc')).account_id")
OBJECT_URL="https://$ACCOUNT_ID.r2.cloudflarestorage.com/$BUCKET/uploads/browser.txt"
curl -i -X OPTIONS -H "Origin: $ORIGIN" -H "Access-Control-Request-Method: PUT" -H "Access-Control-Request-Headers: content-type" "$OBJECT_URL"
Es muss Access-Control-Allow-Origin zurückgegeben werden, dessen Wert dem exakten Origin der Seite entspricht. Fragen Sie nun von einem nicht zugehörigen Origin aus an:
curl -i -X OPTIONS -H "Origin: https://outside.example" -H "Access-Control-Request-Method: PUT" -H "Access-Control-Request-Headers: content-type" "$OBJECT_URL"
Die Antwort darf diesen Origin nicht erlauben, also auch nicht * zurückgeben. Der genaue Status einer Ablehnungsantwort kann abweichen; entscheidend ist, dass die Erlaubnis fehlt und dadurch der Browserzugriff verhindert wird. Versuchen Sie abschließend, das Objekt ohne Signatur zu lesen:
curl -sS -o unsigned.xml -w "%{http_code}\n" "$OBJECT_URL"
Erwarten Sie 400 mit XML-Code InvalidArgument und Message Authorization, wie bei einer vollständig unsignierten Anfrage an diesen R2-S3-Endpunkt beobachtet. Prüfen Sie beide Felder; ein beliebiger Fehler oder Netzwerkfehler beweist keine Signaturablehnung.
python3 - <<'PYXML'
from xml.etree import ElementTree
root = ElementTree.parse("unsigned.xml").getroot()
print("Code:", root.findtext("Code"))
print("Message:", root.findtext("Message"))
PYXML
Das Aktivieren von CORS hat den Bucket nicht öffentlich gemacht und keine Signaturprüfungen entfernt. Ein Nicht-Browser-Client mit einer gültigen Signatur kann den erlaubten Vorgang weiterhin unabhängig von einem Origin-Header verwenden; Origin-Zeichenfolgen sind keine Identitätsnachweise.
Upload-Ressourcen und Berechtigungen entfernen
In diesem Schritt entfernen Sie den UI-Worker, das konkrete Objekt und den Bucket. Bestätigen Sie, dass die Ressourcen nicht mehr vorhanden sind, bevor Sie die Berechtigungen widerrufen.
npx wrangler delete
npx wrangler r2 object delete "$BUCKET/uploads/browser.txt" --remote --env-file=.env.management
npx wrangler r2 bucket delete "$BUCKET" --env-file=.env.management
npx wrangler r2 bucket list --env-file=.env.management
Bestätigen Sie, dass nur die generierten Namen dieses Labors betroffen sind. Aktualisieren Sie die Worker- und Bucket-Liste im Dashboard. Führen Sie die Plattformprüfung zur Bereinigung aus, solange die Verwaltungsautorisierung noch aktiv ist. Die CORS-Konfiguration des Buckets wird zusammen mit dem Bucket entfernt.
Die Laborausweise widerrufen und abmelden
In diesem Schritt schließen Sie den durch diese Übung hinterlassenen Zugriff. Widerrufen Sie auf der Seite für R2-API-Tokens nur das Objekt-Token, das nach diesem Labor benannt ist. Widerrufen Sie auf der API-Token-Seite Ihres Profils das separate R2-Verwaltungstoken, das Sie für dieses Labor erstellt haben. Das Löschen eines Buckets widerruft kein Token, und die Abmeldung von Wrangler widerruft keine S3-Berechtigungen.
Entfernen Sie nach dem Widerruf die lokale Berechtigungsdatei und melden Sie diese VM ab:
rm .env.s3 .env.management put-url.txt
npx wrangler logout
Prüfen Sie die strukturierte Identität. Der Status ungleich null ist im abgemeldeten Zustand zu erwarten:
npx wrangler whoami --json || true
Es muss loggedIn: false angezeigt werden. Lassen Sie Ihre normale Dashboard-Anmeldung bestehen. Die Plattform prüft, ob die lokalen Berechtigungsdateien entfernt wurden und Wrangler abgemeldet ist. Beide Token-Widerrufe sind in diesem Durchlauf manuelle Prüfpunkte im Dashboard; sie werden nicht aus dem Löschen der Dateien abgeleitet.
Zusammenfassung
Konfigurieren Sie CORS für einen exakten Origin, laden Sie synthetische Bytes mit einer presigned URL hoch, unterscheiden Sie Browserberechtigung von Autorisierung und bereinigen Sie anschließend alle Ressourcen.



