Autorización del almacenamiento
Las colecciones están protegidas por la seguridad a nivel de fila; un bucket no tiene un equivalente por objeto, así que el almacenamiento se autoriza aquí. Configuración de Storage cubre los propios backends.
Políticas
Sección titulada «Políticas»La forma declarativa. Una lista de patrones de ruta, que se leen sin ejecutar nada:
storagePolicies: [ { path: "public/**", operations: ["read"], allow: "public" }, { path: "users/:uid/**", allow: ({ params, user }) => user?.uid === params.uid }]Una clave que no coincide con ninguna política se rechaza, a menos que un hook storageAuthorize la permita: al hook solo se le pregunta cuando ninguna política coincidió con la clave para esa operación, y ninguno de los dos puede restringir al otro. Una política que coincidió y no concedió es definitiva. Toda ampliación es una línea explícita, y un error deniega en lugar de conceder.
Los patrones coinciden por segmento, nunca por subcadena — public/** no coincide con
publicity/secret.png:
| Patrón | Coincide con |
|---|---|
avatars/logo.png |
exactamente esa clave |
users/*/avatar.png |
exactamente un segmento donde está * |
users/:uid/** |
un segmento capturado y luego el resto — incluido nada |
** solo es válido como último segmento. :name captura un segmento y nunca
cruza una /; las capturas llegan como params en el predicado.
allow es "public" (cualquiera que la ruta deje pasar), "authenticated" (cualquier llamante con un uid), o un
predicado que recibe los parámetros capturados, el usuario, la operación y el bucket.
Una política se ejecuta después de la autenticación de la ruta, así que "public" por sí solo no abre una ruta a llamantes anónimos. Con requireAuth activo, solo alcanzan lecturas de claves public/, o todas las lecturas con storagePublicRead; /upload, /list, los borrados y las subidas reanudables siempre necesitan un llamante. En otro caso la respuesta es 401.
operations por defecto son las cuatro — read, write, delete, list.
Las políticas satisfacen por sí solas la protección de arranque en producción, y un patrón mal formado hace fallar el arranque en lugar de la primera subida.
El hook
Sección titulada «El hook»requireAuth y publicRead son interruptores globales: deciden si un llamante debe haber iniciado sesión, no qué puede tocar. Sin un hook de autorización, cualquier usuario autenticado puede leer cualquier clave que sepa nombrar — lo único que separa los archivos de dos tenants es que la clave sea imposible de adivinar, lo cual no es un modelo de control de acceso. Peor aún, pueden hacer antes un GET /storage/list?prefix=, así que ni siquiera hace falta adivinar las claves.
storageAuthorize es el análogo en almacenamiento de las reglas de seguridad de una colección, y se ejecuta tras la autenticación en cada ruta de almacenamiento:
await initializeRebaseBackend({ storage: { type: "s3", bucket: "app-files", /* ... */ }, storageAuthorize: async ({ key, bucket, operation, user }) => { if (!user) return false; // Keys are laid out as `{teamId}/{docId}/...` const [teamId] = key.split("/"); return isTeamMember(user.uid, teamId); }});| Campo | Descripción |
|---|---|
key |
Clave del objeto, sin el prefijo del bucket y con el recorrido de rutas (traversal) saneado |
bucket |
Bucket resuelto ("default" cuando no se especifica) |
operation |
"read", "write", "delete" o "list" |
user |
{ uid, email?, roles? }, o null donde la ruta permite el acceso anónimo |
storageId |
El backend con nombre, cuando la petición apuntaba a uno — su clave canónica (storageId=%20media es "media"). Ausente para el origen por defecto, sea cual sea la forma en que la petición lo nombró (sin parámetro, vacío o (default)) |
data |
Acceso de lectura de confianza que omite RLS — data.collection(slug).find(query) / .findById(id). La propiedad reside en una fila, no en un prefijo de clave, así que el hook necesita un lector para responder “¿quién es el propietario de este objeto?”. Omite la seguridad a nivel de fila deliberadamente: este hook es la decisión de autorización, y tomarla a través de un lector ya restringido por los propios permisos del llamante sería circular. Solo lectura por diseño. |
Devuelve false para denegar con un 403. Lanzar una excepción también deniega — una consulta de propiedad que falla no deja pasar la petición.
Conviene saber:
- La ruta de metadatos es donde de verdad se decide el acceso de lectura. Emite el token de descarga de corta duración y limitado a una ruta en el que confía la ruta del archivo, así que el hook la controla ahí. Dos lecturas se saltan el hook, por diseño. Una que lleva un token así: el hook la aprobó cuando se emitió el token, solo para esa ruta y ese origen, así que un hook que ahora la rechazaría (una pertenencia revocada desde entonces) no la detiene antes de que el token caduque —
storageDownloadTokenTtl, 300 segundos por defecto. Y la lectura de una clave bajopublic/por un llamante sin credencial: el prefijo es la declaración de que cualquiera puede leerla, incluidos visitantes anónimos y CDNs. La lectura de esa clave por un llamante con sesión iniciada sigue consultándose, pero la de uno anónimo no, así que un hook no puede volver privada una clave depublic/; guarda el archivo fuera depublic/en su lugar. Solo esas dos lecturas se lo saltan: a un usuario que tu adaptador de autenticación llamepublicse le consulta como a cualquier otro. - Un bucket, un nombre. En S3 y GCS el
bucketdel hook es siempre"default", también cuando una petición nombra el bucket del proveedor, que llega a los mismos objetos. - Una ruta es pública cuando lo es la clave que nombra. El prefijo
public/se comprueba sobre la clave canónica que sirve la ruta — después de leer un segmento de bucketdefault/inicial y de eliminar//,./y una/inicial — nunca sobre la URL tal como está escrita./api/storage/file/notes://public/x.txtnombra la clave privadanotes:/public/x.txt, y la clavedefault/public/x.txt(a la que se llega como/api/storage/file/default/default/public/x.txt) está en una carpeta llamadadefault; ninguna es pública. En un hook, comprueba lakeyque se te entrega conisPublicStorageKeyde@rebasepro/types—isPublicStoragePathlee una ruta o URL del lado del cliente, y lee ambas como públicas. listse controla sobre el prefijo. Listar es la forma de descubrir claves de las que nadie te habló.- Las subidas reanudables (TUS) se controlan al crearse, así que una subida denegada no deja ningún archivo temporal.
- Omitir el hook (y
storagePolicies) no deja ninguna comprobación por objeto, así que producción se niega a arrancar a menos que se definastoragePublicReadostorageInsecureAllowAnyAuthenticated.
Claves de API y tokens
Sección titulada «Claves de API y tokens»Una persona con sesión iniciada tiene todos los alcances de almacenamiento, así que deciden por ella el hook
y las políticas de arriba. Una clave de API necesita además el alcance de
almacenamiento de la operación, sobre el origen que nombra la petición: storage:read para
descargas, metadatos y listados, storage:write para subidas, carpetas y cada
paso de una subida reanudable, storage:delete para eliminaciones. El destino es el
id del origen, y el del origen por defecto es (default): storage:read:(default),
storage:write:avatars. Sin él, la respuesta es 403 SCOPE_MISSING. Con él,
el hook sigue ejecutándose, con la identidad con la que actúa la clave.
Siguientes pasos
Sección titulada «Siguientes pasos»- Configuración de Storage — backends, endpoints y límites
- Claves de API — los alcances que lleva una clave