Introducción
Un agente de soporte necesita compartir brevemente una exportación privada sintética sin hacer público su bucket ni entregar credenciales de almacenamiento. Usted emitirá una URL de descarga prefirmada, observará su caducidad y comprobará que la credencial de firma no puede acceder a otro bucket propiedad del laboratorio.
Complete primero Organize a Document Bucket and Stream Documents Through a Worker. Esta máquina virtual independiente tiene Node.js 22.22.0, Wrangler 4.131.1 y AWS SDK 3.888.0 fijados. La cuenta debe tener R2 activo y permiso para crear un token de objeto de usuario con alcance restringido. Solo se utilizan dos archivos sintéticos pequeños. Consulte precios de R2 y acceso prefirmado; no necesita comprar un dominio. No comparta los enlaces ni las credenciales generados. Vacíe ambos buckets y revoque únicamente los tokens de este laboratorio antes de salir.
Cree buckets aislados para probar el acceso
En este paso, autorizará esta máquina virtual y creará un bucket desechable. La autorización del dispositivo confirma su cuenta de aprendizaje. La administración de buckets de R2 utiliza un token de API independiente, restringido a esa cuenta.
Inicie Bash para usar la sintaxis de comandos indicada a continuación. Después, vaya al proyecto preparado y compruebe sus herramientas. Mantenga abierto este mismo terminal para conservar disponibles las variables de nombres de recursos:
bash
cd /home/labex/project/r2-lab
export PATH="$PWD/.tools/node-v22.22.0-linux-x64/bin:$PATH"
node --version
npx wrangler --version
Autorice el código de dispositivo mostrado en su propio navegador. Confirme la cuenta de aprendizaje y los alcances de lectura de cuenta y usuario solicitados antes de dar su consentimiento:
npx wrangler login --device --browser=false --scopes account:read user:read
npx wrangler whoami --json
Exija loggedIn: true. Lea el nombre de la cuenta aunque solo aparezca una. Sustituya YOUR_ACCOUNT_ID a continuación por el ID real de 32 caracteres de esa cuenta. openssl rand -hex 6 genera doce caracteres hexadecimales aleatorios para evitar que este laboratorio entre en conflicto con una ejecución anterior. El documento aquí incrustado escribe un archivo de configuración estándar; el shell sustituye en él sus variables.
ACCOUNT_ID=YOUR_ACCOUNT_ID
RUN_ID=$(openssl rand -hex 6)
NAME="labex-c05-r04-$RUN_ID"
BUCKET="$NAME-docs"
cat > wrangler.jsonc <<JSON
{"name":"$NAME","account_id":"$ACCOUNT_ID","compatibility_date":"2026-07-30","r2_buckets":[{"binding":"DOCUMENTS","bucket_name":"$BUCKET"}]}
JSON
Para administrar el bucket, abra la página API Tokens de su perfil de Cloudflare y cree un token personalizado con el nombre de este laboratorio. Conceda Account → Workers R2 Storage → Edit y restrinja Account Resources a la cuenta de aprendizaje cuyo ID guardó. Establezca una caducidad breve. No incluya otras cuentas ni permisos no relacionados. Este permiso a nivel de cuenta permite crear y eliminar buckets; el token que solo opera sobre objetos del paso siguiente no puede hacerlo.
Copie el token una sola vez en este indicador oculto de la máquina virtual. umask 077 restringe el archivo a su usuario; read -s oculta la entrada. El archivo utiliza la variable de token estándar de Wrangler y queda excluido de Git.
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
Use --env-file=.env.management únicamente para los comandos de administración de R2; whoami seguirá comprobando la autorización del dispositivo de la máquina virtual.
Coloque --env-file al final de cada comando de Wrangler para que su lista de archivos no incluya el nombre del comando. Después de crear cada bucket, si Wrangler pregunta si debe añadir un enlace a la configuración, escriba n y pulse Enter. La configuración ya contiene el enlace previsto.
npx wrangler r2 bucket create "$BUCKET" --env-file=.env.management
Enumere sus buckets y busque el nombre generado exacto. Los demás buckets pertenecen a otros trabajos; no los modifique.
npx wrangler r2 bucket list --env-file=.env.management
En el Dashboard, abra Storage & databases → R2 → Overview, seleccione este bucket exacto e inspeccione su lista de objetos vacía. En su configuración, mantenga desactivadas la URL de desarrollo pública y los dominios personalizados. El nombre del bucket en el Dashboard confirma su identidad; las comprobaciones de descarga posteriores demostrarán que los bytes están almacenados.
Cree un segundo bucket propiedad de este laboratorio. Representa un límite de almacenamiento no relacionado; no sustituya un bucket de otro ejercicio. Cargue un archivo sintético en cada bucket con la credencial de administración:
CONTROL="$NAME-control"
npx wrangler r2 bucket create "$CONTROL" --env-file=.env.management
npx wrangler r2 object put "$BUCKET/documents/report.txt" --remote --file document.txt --content-type text/plain --env-file=.env.management
npx wrangler r2 object put "$CONTROL/retained/handbook.txt" --remote --file retained.txt --content-type text/plain --env-file=.env.management
Mantenga desactivado el acceso público en ambos buckets. A continuación, permitirá que la nueva credencial lea únicamente el bucket de documentos.
Restrinja la credencial de firma
En este paso, creará una credencial de solo lectura restringida a un bucket. Una URL prefirmada no puede conceder más acceso que la credencial utilizada para firmarla. El token de administración puede administrar ambos buckets del laboratorio, por lo que no debe utilizarlo como identidad de firma en esta lección.
La API compatible con S3 permite que los SDK de almacenamiento estándar accedan a R2. Utiliza un par de claves de acceso independiente en lugar del token de dispositivo de Wrangler. En R2 Overview, use Account Details → API Tokens → Manage y cree un User API token con el nombre del recurso generado para este laboratorio. Elija Object Read only, restrínjalo a este bucket nuevo exacto y seleccione una caducidad breve si el formulario ofrece esa opción. No elija todos los buckets ni el acceso Admin. Mantenga abierta esta página del token hasta haber guardado el secreto de un solo uso.
Utilice los siguientes indicadores de Bash en la máquina virtual. read -s oculta la entrada; umask 077 hace que el archivo de credenciales solo pueda leerlo su usuario. Estos nombres son las variables de entorno estándar del AWS SDK. Pegue el Access Key ID y el Secret Access Key en sus respectivos indicadores y pulse Enter. No pegue el valor del token de API general.
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
Escriba un cliente reutilizable para el SDK estándar. El SDK requiere una cadena de región; R2 utiliza auto. Leer la configuración existente mantiene las operaciones de la CLI y del SDK dirigidas a la misma cuenta y al mismo bucket.
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",
endpoint: `https://${config.account_id}.r2.cloudflarestorage.com`,
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY
}
});
JS
Seleccione únicamente el bucket -docs; mantenga el bucket -control fuera del alcance de recursos de este token. El cliente existente de storage.mjs solo lee .env.s3 cuando se incluye --env-file=.env.s3. No utilice el permiso Admin ni el alcance para todos los buckets.
Antes de compartir cualquier URL, confirme el bucket seleccionado en el formulario del token. La solicitud real al bucket de control del paso siguiente comprobará de forma independiente que este límite funciona.

Este ejemplo selecciona Object Read only y solo el bucket -docs generado; excluye el bucket -control. El nombre generado será diferente. El TTL del token de 24 horas es independiente de la caducidad de cinco minutos o cinco segundos de cada URL firmada. El formulario muestra el alcance elegido; las solicitudes remotas aceptadas y rechazadas demuestran los permisos reales.
Firme y pruebe descargas temporales
En este paso, firmará solicitudes GET para un objeto específico y un intervalo de tiempo determinado. La firma se ejecuta localmente sin ponerse en contacto con R2; crear una URL no demuestra que tenga permiso. R2 evalúa la firma cuando un cliente utiliza la URL. Cualquiera que posea la URL puede usar la operación concedida hasta que caduque, por lo que debe mantener estos enlaces fuera de registros, mensajes y capturas de pantalla.
Escriba un programa pequeño para firmar. expiresIn se mide en segundos. El archivo guarda una URL de cinco minutos, una URL de cinco segundos y una URL deliberadamente fuera de alcance, sin mostrarlas:
cat > sign.mjs <<'JS'
import { GetObjectCommand } from "@aws-sdk/client-s3";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
import { readFileSync, writeFileSync } from "node:fs";
import { s3, Bucket } from "./storage.mjs";
const config = JSON.parse(readFileSync("wrangler.jsonc", "utf8"));
const command = new GetObjectCommand({ Bucket, Key: "documents/report.txt" });
const allowed = await getSignedUrl(s3, command, { expiresIn: 300 });
const short = await getSignedUrl(s3, command, { expiresIn: 5 });
const outside = await getSignedUrl(s3, new GetObjectCommand({
Bucket: config.name + "-control", Key: "retained/handbook.txt"
}), { expiresIn: 300 });
const unsigned = new URL(allowed); unsigned.search = "";
writeFileSync("signed.json", JSON.stringify({ allowed, short, outside, unsigned: unsigned.href }), { mode: 0o600 });
console.log("Stored temporary request URLs in private signed.json");
JS
node --env-file=.env.s3 sign.mjs
Utilice enseguida la URL permitida. Esta solicitud curl no contiene ninguna cabecera Authorization: la URL lleva la firma. node -p proporciona directamente a curl la URL almacenada sin mostrarla en la salida del terminal.
curl -fsS "$(node -p "require('./signed.json').allowed")" -o signed-download.txt
cmp document.txt signed-download.txt
Exija que los bytes sean idénticos. Ahora espere diez segundos para que la URL de cinco segundos caduque y, después, inspeccione las respuestas de denegación. Estas solicitudes omiten intencionadamente --fail para mostrar los errores HTTP:
sleep 10
curl -sS -o expired.xml -w "%{http_code}\n" "$(node -p "require('./signed.json').short")"
curl -sS -o unsigned.xml -w "%{http_code}\n" "$(node -p "require('./signed.json').unsigned")"
curl -sS -o outside.xml -w "%{http_code}\n" "$(node -p "require('./signed.json').outside")"
Exija 403 para las solicitudes caducadas y fuera del alcance permitido. La solicitud sin firma a este endpoint S3 de R2 devuelve 400 con InvalidArgument y el mensaje Authorization: falta la información de firma requerida. Esto es distinto de denegar a una credencial con firma válida el acceso a otro bucket. Inspeccione los nombres de error sin imprimir los documentos de respuesta completos, ya que pueden contener detalles de la solicitud:
python3 - <<'PYXML'
from xml.etree import ElementTree
for path in ["expired.xml", "unsigned.xml", "outside.xml"]:
root = ElementTree.parse(path).getroot()
print(path, root.findtext("Code"))
PYXML
La respuesta de expiración debe identificar la caducidad, mientras que el bucket de control debe devolver AccessDenied. Un fallo de red no es un resultado de autorización. Si tardó más de cinco minutos antes de probar el bucket de control, regenere las URL, repita las solicitudes permitida y fuera de alcance y, después, vuelva a dejar que caduque la URL corta.
Distinga entre permisos de URL y acceso público
En este paso, relacionará las solicitudes observadas con el modelo de acceso. El enlace que funcionó autorizó una solicitud GET firmada para una clave concreta. No habilitó la URL de desarrollo pública del bucket, no autorizó PUT ni inició sesión de un usuario en su aplicación.
Abra la configuración de ambos buckets propiedad del laboratorio en el Dashboard. Mantenga desactivadas las URL de desarrollo públicas y sin dominios personalizados. En la página del token de objetos, confirme que el token de solo lectura cubra únicamente el bucket -docs. No revele su secreto de un solo uso en una captura de pantalla.
Ejecute ahora la comprobación de la plataforma. Genera sus propias solicitudes firmadas, exige que los bytes permitidos sean exactos y verifica las denegaciones de solicitudes sin firma, caducadas y fuera de alcance. Esta es una evidencia real de la API remota; el archivo signed.json no es un indicador de finalización.
En una aplicación de producción, decida si un usuario puede acceder al documento antes de emitir un enlace firmado. Una vez emitido, el enlace portador se puede copiar. El laboratorio posterior sobre autorización de aplicaciones aborda directamente esa decisión. Las URL S3 firmadas se dirigen al endpoint S3 de R2 de la cuenta, no a una dirección pública r2.dev ni a un dominio personalizado.
Limpie ambos buckets propiedad del laboratorio
En este paso, eliminará las dos claves de prueba explícitas y, después, sus buckets. Mantenga activa la credencial de administración hasta que la plataforma confirme que ambos nombres de bucket han desaparecido.
BUCKET=$(node -p "JSON.parse(require('fs').readFileSync('wrangler.jsonc')).r2_buckets[0].bucket_name")
CONTROL=$(node -p "JSON.parse(require('fs').readFileSync('wrangler.jsonc')).name + '-control'")
npx wrangler r2 object delete "$BUCKET/documents/report.txt" --remote --env-file=.env.management
npx wrangler r2 object delete "$CONTROL/retained/handbook.txt" --remote --env-file=.env.management
npx wrangler r2 bucket delete "$BUCKET" --env-file=.env.management
npx wrangler r2 bucket delete "$CONTROL" --env-file=.env.management
npx wrangler r2 bucket list --env-file=.env.management
Confirme cada nombre exacto cuando se le solicite. Una lista de buckets correcta debe omitir ambos nombres. Nunca elimine otro bucket para que el inventario global quede vacío.
Revoque la credencial del laboratorio y cierre la sesión
En este paso, cerrará el acceso que dejó este ejercicio. En la página R2 API Tokens, revoque únicamente el token de objetos cuyo nombre corresponde a este laboratorio. En la página API Tokens de su perfil, revoque el token de administración de R2 independiente que creó para este laboratorio. Eliminar un bucket no revoca un token, y cerrar la sesión de Wrangler no revoca las credenciales S3.
Después de revocar las credenciales, elimine el archivo local de credenciales y cierre la sesión de esta máquina virtual:
rm .env.s3 .env.management signed.json
npx wrangler logout
Inspeccione la identidad estructurada. Es normal que el estado sea distinto de cero al cerrar la sesión:
npx wrangler whoami --json || true
Exija loggedIn: false; mantenga abierta su sesión habitual del Dashboard. La plataforma comprueba la eliminación de las credenciales locales y el cierre de sesión de Wrangler. Las dos revocaciones de tokens son puntos de control manuales en el Dashboard en esta versión candidata; no se deducen de la eliminación de archivos.
Resumen
Cree acceso restringido a archivos privados, compruebe los límites de éxito y denegación de las URL prefirmadas, distinga entre acceso público y autorización, y revoque las credenciales del laboratorio.



