Regole di sicurezza Firebase per riferimento al cloud storage

Le regole di sicurezza Firebase per Cloud Storage vengono utilizzate per determinare chi ha accesso in lettura e scrittura ai file archiviati in Cloud Storage, nonché come sono strutturati i file e quali metadati contengono. Le regole di sicurezza di Cloud Storage sono composte da regole che prendono in considerazione la request e resource per consentire o negare un'azione desiderata, come il caricamento di un file o il recupero dei metadati del file. Questi documenti di riferimento riguardano i tipi di regole, le proprietà di una request e di una resource , i tipi di dati utilizzati dalle regole di sicurezza di Cloud Storage e il modo in cui si verificano gli errori.

Regola

Una rule è un'espressione che viene valutata per determinare se una request può eseguire l'azione desiderata.

Tipi

Permettere

le regole allow sono costituite da un metodo, ad esempio read o write , nonché da una condizione facoltativa. Quando viene eseguita una regola, la condizione viene valutata e, se la condizione restituisce true , viene consentito il metodo desiderato; in caso contrario, il metodo viene negato. Una regola allow senza condizione consente sempre il metodo desiderato.

// Always allow method
allow <method>;

// Allow method if condition is true
allow <method>: if <condition>;

Attualmente, allow è l'unico tipo di regola supportato.

Metodi di richiesta

Leggere

Il metodo read copre tutte le richieste in cui vengono letti i dati o i metadati dei file, inclusi i download di file e le letture dei metadati dei file.

// Always allow reads
allow read;

// Allow reads if condition evaluates to true
allow read: if <condition>;

Scrivere

Il metodo write copre tutte le richieste in cui vengono scritti dati o metadati di file, inclusi caricamenti di file, eliminazioni di file e aggiornamenti di metadati di file.

// Always allow writes
allow write;

// Allow writes if condition evaluates to true
allow write: if <condition>;

Incontro

Le regole vengono eseguite quando una request dell'utente (ad esempio il caricamento o il download di un file) corrisponde a un percorso di file coperto da una regola. Una match è costituita da un percorso e da un corpo, che deve contenere almeno una regola allow . Se non viene trovata alcuna corrispondenza, la richiesta viene rifiutata.

È possibile match un percorso con nome completo oppure inserire caratteri jolly per abbinare tutti i percorsi che rientrano in un determinato modello.

Segmenti di percorso

single_segment

Puoi utilizzare segmenti di percorso singolo per creare una regola che corrisponda a un file archiviato in Cloud Storage.

// Allow read at "path" if condition evaluates to true
match /path {
  allow read: if <condition>;
}

Sono consentiti anche più segmenti di percorso e percorsi nidificati:

// Allow read at "path/to/object" if condition evaluates to true
match /path {
  match /to {
    match /object {
      allow read: if <condition>;
    }
  }
}

{single_segment_wildcard}

Se desideri applicare una regola a più file nello stesso percorso, puoi utilizzare un segmento di percorso con caratteri jolly per abbinare tutti i file in un determinato percorso. Una variabile jolly viene dichiarata in un percorso racchiudendo una variabile tra parentesi graffe: {variable} . Questa variabile è accessibile all'interno dell'istruzione match come una string .

// Allow read at any path "/*", if condition evaluates to true
match /{single_path} {
  // Matches "path", "to", or "object" but not "path/to/object"
  allow read: if <condition>;
}

Anche più segmenti di percorso e percorsi nidificati possono contenere caratteri jolly:

// Allow read at any path "/path/*/newPath/*", if condition evaluates to true
match /path/{first_wildcard} {
  match /newPath/{second_wildcard} {
    // Matches "path/to/newPath/newObject" or "path/from/newPath/oldObject"
    allow read: if <condition>;
  }
}

{multi_segment_wildcard=**}

Se desideri far corrispondere un numero qualsiasi di segmenti di percorso in corrispondenza o al di sotto di un percorso, puoi utilizzare un carattere jolly multisegmento, che corrisponderà a tutte le richieste verso e sotto la posizione. Ciò può essere utile per fornire a un utente il proprio spazio di archiviazione in formato libero o per creare regole che corrispondano a molti segmenti di percorso diversi (come la creazione di un set di file leggibili pubblicamente o la richiesta di autenticazione per tutte le scritture).

Un percorso con caratteri jolly a più segmenti viene dichiarato in modo simile a un carattere jolly a segmento singolo, con l'aggiunta di =** alla fine della variabile: {variable=**} . Una variabile jolly multisegmento è disponibile all'interno dell'istruzione match come oggetto path .

// Allow read at any path "/**", if condition evaluates to true
match /{multi_path=**} {
  // Matches anything at or below this, from "path", "path/to", "path/to/object", ...
  allow read: if <condition>;
}

Richiesta

La variabile request viene fornita all'interno di una condizione per rappresentare la richiesta effettuata su quel percorso. La variabile request ha una serie di proprietà che possono essere utilizzate per decidere se consentire la richiesta in arrivo.

Proprietà

auth

Quando un utente autenticato esegue una richiesta su Cloud Storage, la variabile auth viene popolata con l' uid dell'utente ( request.auth.uid ) e con le attestazioni del JWT di autenticazione Firebase ( request.auth.token ).

request.auth.token contiene alcune o tutte le seguenti chiavi:

Campo Descrizione
email L'indirizzo email associato all'account, se presente.
email_verified true se l'utente ha verificato di avere accesso all'indirizzo email . Alcuni provider verificano automaticamente gli indirizzi email di loro proprietà.
phone_number Il numero di telefono associato all'account, se presente.
name Il nome visualizzato dell'utente, se impostato.
sub L'UID Firebase dell'utente. Questo è unico all'interno di un progetto.
firebase.identities Dizionario di tutte le identità associate all'account di questo utente. I tasti del dizionario possono essere uno dei seguenti: email , phone , google.com , facebook.com , github.com , twitter.com . I valori del dizionario sono matrici di identificatori univoci per ciascun provider di identità associato all'account. Ad esempio, auth.token.firebase.identities["google.com"][0] contiene il primo ID utente Google associato all'account.
firebase.sign_in_provider Il provider di accesso utilizzato per ottenere questo token. Può essere una delle seguenti stringhe: custom , password , phone , anonymous , google.com , facebook.com , github.com , twitter.com .
firebase.tenant Il tenantId associato all'account, se presente. ad esempio tenant2-m6tyz

Se si utilizza l'autenticazione personalizzata, request.auth.token contiene anche eventuali attestazioni personalizzate specificate dallo sviluppatore.

Quando un utente non autenticato esegue una richiesta, request.auth è null .

// Allow requests from authenticated users
allow read, write: if request.auth != null;

path

La variabile path contiene il percorso rispetto al quale viene eseguita una request .

// Allow a request if the first path segment equals "images"
allow read, write: if request.path[0] == 'images';

resource

La variabile resource contiene i metadati di un file in fase di caricamento o i metadati aggiornati per un file esistente. Ciò è correlato alla variabile resource , che contiene i metadati del file corrente nel percorso richiesto, anziché i nuovi metadati.

// Allow a request if the new value is smaller than 5MB
allow read, write: if request.resource.size < 5 * 1024 * 1024;

request.resource contiene le seguenti proprietà della resource :

Proprietà
name
bucket
metadata
size
contentType

time

La variabile time contiene un timestamp che rappresenta l'ora corrente del server in cui viene valutata una richiesta. È possibile utilizzarlo per fornire un accesso ai file basato sul tempo, ad esempio: consentire il caricamento dei file solo fino a una determinata data o consentire la lettura dei file solo fino a un'ora dopo il caricamento.

// Allow a read if the file was created less than one hour ago
allow read: if request.time < resource.timeCreated + duration.value(1, 'h');

Sono fornite molte funzioni per scrivere regole utilizzando timestamp e durate .

Risorsa

La variabile della resource contiene metadati per i file in Cloud Storage, come nome file, dimensione, ora di creazione e metadati personalizzati.

Proprietà

name

Una stringa contenente il nome completo del file, incluso il percorso del file.

// Allow reads if the resource name is "path/to/object"
allow read: if resource.name == 'path/to/object'

bucket

Una stringa contenente il bucket Google Cloud Storage in cui è archiviato questo file.

// Allow reads of all resources in your bucket
allow read: if resource.bucket == '<your-cloud-storage-bucket>'

generation

Un int contenente la generazione dell'oggetto Google Cloud Storage del file. Utilizzato per il controllo delle versioni degli oggetti.

// Allow reads if the resource matches a known object version
allow read: if resource.generation == <known-generation>

metageneration

Un int contenente la metagenerazione dell'oggetto Google Cloud Storage del file. Utilizzato per il controllo delle versioni degli oggetti.

// Allow reads if the resource matches a known object metadata version
allow read: if resource.metageneration == <known-generation>

size

Un int contenente la dimensione del file in byte.

// Allow reads if the resource is less than 10 MB
allow read: if resource.size < 10 * 1024 * 1024;

timeCreated

Un timestamp che rappresenta il momento in cui è stato creato il file.

// Allow reads if the resource was created less than an hour ago
allow read: if resource.timeCreated < request.time + duration.value(60, "m")

updated

Un timestamp che rappresenta l'ultimo aggiornamento del file.

// Allow reads if the resource was updated less than an hour ago
allow read: if resource.updated < request.time + duration.value(60, "m")

md5Hash

Una stringa contenente l' hash MD5 del file.

// Allow writes if the hash of the uploaded file is the same as the existing file
allow write: if request.resource.md5Hash == resource.md5Hash;

crc32c

Una stringa contenente l' hash crc32c del file.

// Allow writes if the hash of the uploaded file is the same as the existing file
allow write: if request.resource.crc32c == resource.crc32c;

etag

Una stringa contenente l' etag del file.

// Allow writes if the etag matches a known object etag
allow write: if resource.etag == <known-generation>

contentDisposition

Una stringa contenente la disposizione del contenuto del file.

// Allow reads if the content disposition matches a certain value
allow read: if resource.contentDisposition == 'inlined';

contentEncoding

Una stringa contenente la codifica del contenuto del file.

// Allow reads if the content is encoded with gzip
allow read: if resource.contentEncoding == 'gzip';

contentLanguage

Una stringa contenente la lingua del contenuto del file.

// Allow reads if the content language is Japanese
allow read: if resource.contentLanguage == 'ja';

contentType

Una stringa contenente il tipo di contenuto del file.

// Allow reads if the content type is PNG.
allow read: if resource.contentType == 'image/png';

metadata

Un Map<String, String> contenente ulteriori campi di metadati forniti dallo sviluppatore.

// Allow reads if a certain metadata field matches a desired value
allow read: if resource.metadata.customProperty == 'customValue';

firestore.get e firestore.exist

Le funzioni firestore.get() e firestore.exists() consentono di accedere ai documenti in Cloud Firestore per valutare criteri di autorizzazione complessi.

Le funzioni firestore.get() e firestore.exists() prevedono entrambe percorsi di documenti completamente specificati. Quando si utilizzano variabili per costruire percorsi per firestore.get() e firestore.exists() , è necessario eseguire l'escape esplicito delle variabili utilizzando la sintassi $(variable) .

firestore.get

Ottieni il contenuto di un documento Cloud Firestore.

service firebase.storage {
  match /b/{bucket}/o {
    match /users/{club}/files/{fileId} {
      allow read: if club in
        firestore.get(/databases/(default)/documents/users/$(request.auth.uid)).data.memberships
    }
  }
}

firestore.exists

Controlla se esiste un documento Cloud Firestore.

service firebase.storage {
  match /b/{bucket}/o {
    match /users/{userId}/photos/{fileId} {
      allow read: if
        firestore.exists(/databases/(default)/documents/users/$(userId)/friends/$(request.auth.uid))
    }
  }
}

Servizio

Il service è la prima dichiarazione in un file di regole di sicurezza di Cloud Storage e specifica a quale servizio verranno applicate queste regole.

Nome

name

Verrà applicato il nome delle regole del servizio. L'unico valore corrente è firebase.storage .

// Specify the service name
service firebase.storage {
  match /b/{bucket}/o {
    ...
  }
}

Tipi di dati

Il linguaggio Rules consente di verificare il tipo utilizzando l'operatore is .

// For example
a is null
a is string

null

Il tipo di dati null rappresenta un valore non esistente.

allow read: if request.auth != null;

bool

Il tipo bool rappresenta un valore booleano true o false .

allow read: if true;   // always succeeds
allow write: if false; // always fails

Confronto

I valori booleani possono essere confrontati utilizzando gli operatori == != .

Operazioni booleane

Operazione Espressione
AND x && y
OR x || y
NOT !x

Le operazioni cortocircuitano e possono restituire true , false o Error .

allow read: if true || false;   // always succeeds, short circuits at true
allow write: if false && true; // always fails, short circuits at false

int e float

I tipi int e float rappresentano i numeri. Gli interi sono: 0 , 1 , -2 , ecc. , mentre i float sono: 1.0 , -2.0 , 3.33 , ecc.

Gli int sono valori a 64 bit con segno e i float sono valori conformi a IEEE 754 a 64 bit. I valori di tipo int verranno forzati a float quando utilizzati in confronti e operazioni aritmetiche con un valore float .

Confronto

Gli interi e i float possono essere confrontati e ordinati utilizzando gli operatori == , != , > , < , >= e <= .

Aritmetica

Gli interi e i float possono essere aggiunti, sottratti, moltiplicati, divisi, modulizzati e negati:

Operazione Espressione
Aggiunta x + y
Sottrazione x - y
Moltiplicazione x * y
Divisione x / y
Modulo x % y
Negazione -x

Funzioni matematiche

Firebase Security Rules for Cloud Storage fornisce anche una serie di funzioni di supporto matematico per semplificare le espressioni:

Funzione Descrizione
math.ceil(x) Soffitto del valore numerico
math.floor(x) Piano del valore numerico
math.round(x) Arrotondare il valore di input all'interno più vicino
math.abs(x) Valore assoluto dell'input
math.isInfinite(x) Verifica se il valore è ±∞ , restituisce un bool
math.isNaN(x) Verifica se il valore non è un numero NaN , restituisce un bool

string

Confronto

Le stringhe possono essere confrontate e ordinate lessograficamente utilizzando gli operatori == , != , > , < , >= e <= .

Concatenazione

Le stringhe possono essere concatenate utilizzando l'operatore + .

// Concatenate a file name and extension
'file' + '.txt'

Indice e intervallo

L'operatore index , string[] , restituisce una stringa che contiene il carattere nell'indice fornito nella stringa.

// Allow reads of files that begin with 'a'
match /{fileName} {
  allow read: if fileName[0] == 'a';
}

L'operatore range , string[i:j] , restituisce una stringa che contiene i caratteri tra gli indici specificati, da i (incluso) fino a j (esclusivo). Se i o j non sono specificati, il valore predefinito è rispettivamente 0 e la dimensione della stringa, ma è necessario specificare almeno i o j affinché l'intervallo sia valido.

// Allow reads of files that begin with 'abcdef'
match /{fileName} {
  allow read: if fileName[0:6] == 'abcdef';
}

Gli operatori index e range genereranno un errore se gli indici forniti superano i limiti della stringa.

size

Restituisce il numero di caratteri nella stringa.

// Allow files with names less than 10 characters
match /{fileName} {
  allow write: if fileName.size() < 10;
}

matches

Esegue una corrispondenza con l'espressione regolare, restituisce true se la stringa corrisponde all'espressione regolare data. Utilizza la sintassi Google RE2 .

// Allow writes to files which end in ".txt"
match /{fileName} {
  allow write: if fileName.matches('.*\\.txt')
}

split

Divide una stringa in base a un'espressione regolare fornita e restituisce un list di stringhe. Utilizza la sintassi Google RE2 .

// Allow files named "file.*" to be uploaded
match /{fileName} {
  allow write: if fileName.split('.*\\..*')[0] == 'file'
}

path

I percorsi sono nomi simili a directory con corrispondenza di modelli opzionale. La presenza di una barra / denota l'inizio di un segmento di percorso.

path

Converte un argomento string in un path .

// Allow reads on a specific file path
match /{allFiles=**} {
  allow read: if allFiles == path('/path/to/file');
}

timestamp

I timestamp sono in UTC, con valori possibili che iniziano a 0001-01-01T00.00.00Z e terminano a 9999-12-31T23.59.59Z.

Confronto

I timestamp possono essere confrontati e ordinati utilizzando gli operatori == , != , > , < , >= e <= .

Aritmetica

I timestamp supportano l'addizione e la sottrazione tra timestamp e durate come segue:

Espressione Risultato
timestamp + duration timestamp
duration + timestamp timestamp
timestamp - duration timestamp
timestamp - timestamp duration
duration + duration duration
duration - duration duration

date

Un valore timestamp contenente solo year , month e day .

// Allow reads on the same day that the resource was created.
allow read: if request.time.date() == resource.timeCreated.date()

year

Il valore dell'anno come int, da 1 a 9999.

// Allow reads on all requests made before 2017
allow read: if request.time.year() < 2017

month

Il valore del mese come int, da 1 a 12.

// Allow reads on all requests made during the month of January
allow read: if request.time.month() == 1;

day

Il giorno corrente del mese come int, da 1 a 31.

// Allow reads on all requests made during the first day of each month
allow read: if request.time.day() == 1;

time

Un valore duration contenente l'ora corrente.

// Allow reads on all requests made before 12PM
allow read: if request.time.time() < duration.time(12, 0, 0, 0);

hours

Il valore delle ore è un int, da 0 a 23.

// Allow reads on all requests made before 12PM
allow read: if request.time.hours() < 12;

minutes

Il valore dei minuti è un int, da 0 a 59.

// Allow reads during even minutes of every hour
allow read: if request.time.minutes() % 2 == 0;

seconds

Il valore dei secondi è un int, da 0 a 59.

// Allow reads during the second half of each minute
allow read: if request.time.seconds() > 29;

nanos

I secondi frazionari in nano come int.

// Allow reads during the first 0.1 seconds of each second
allow read: if request.time.nanos() < 100000000;

dayOfWeek

Il giorno della settimana, dall'1 (lunedì) alle 7 (domenica).

// Allow reads on weekdays (Monday to Friday)
allow read: if request.time.dayOfWeek() < 6;

dayOfYear

Il giorno dell'anno corrente, da 1 a 366.

// Allow reads every fourth day
allow read: if request.time.dayOfYear() % 4 == 0;

toMillis

Restituisce il numero corrente di millisecondi dall'epoca di Unix.

// Allow reads if the request is made before a specified time
allow read: if request.time.toMillis() < <milliseconds>;

duration

I valori di durata sono rappresentati come secondi più frazioni di secondo in nanosecondi.

Confronto

Le durate possono essere confrontate e ordinate utilizzando gli operatori == , != , > , < , >= e <= .

Aritmetica

Le durate supportano l'addizione e la sottrazione tra timestamp e durate come segue:

Espressione Risultato
timestamp + duration timestamp
duration + timestamp timestamp
timestamp - duration timestamp
timestamp - timestamp duration
duration + duration duration
duration - duration duration

seconds

Il numero di secondi nella durata corrente. Deve essere compreso tra -315.576.000.000 e +315.576.000.000 inclusi.

nanos

Il numero di frazioni di secondo (in nanosecondi) della durata corrente. Deve essere compreso tra -999.999.999 e +999.999.999 inclusi. Per secondi diversi da zero e nanonsecondi diversi da zero, i segni di entrambi devono essere in accordo.

duration.value

Le durate possono essere create utilizzando la funzione duration.value(int magnitude, string units) , che crea una durata temporale dalla grandezza e dall'unità specificate.

// All of these durations represent one hour:
duration.value(1, "h")
duration.value(60, "m")
duration.value(3600, "s")

unit possibili sono:

Durata unit
Settimane w
Giorni d
Ore h
Minuti m
Secondi s
Millisecondi ms
Nanosecondi ns

duration.time

Le durate possono essere create utilizzando la funzione duration.time(int hours, int minutes, int seconds, int nanoseconds) , che crea una durata temporale di ore, minuti, secondi e nanosecondi specificati.

// Create a four hour, three minute, two second, one nanosecond duration
duration.time(4, 3, 2, 1)

list

Una lista contiene un array ordinato di valori, che può essere di tipo: null , bool , int , float , string , path , list , map , timestamp o duration .

Dati x e y di tipo list e i e j di tipo int

Creazione

Per creare un elenco, aggiungi valori tra parentesi:

// Create a list of strings
['apples', 'grapes', 'bananas', 'cheese', 'goats']

Confronto

Gli elenchi possono essere confrontati utilizzando gli operatori == != . L'uguaglianza di due elenchi richiede che tutti i valori siano uguali.

Indice e intervallo

L'operatore index , list[] , restituisce l'elemento in corrispondenza dell'indice fornito nell'elenco.

// Allow reads of all files that begin with 'a'
match /{fileName} {
  allow read: if fileName[0] == 'a';
}

L'operatore range , list[i:j] , restituisce tutti gli elementi in un elenco tra gli indici specificati, da i (incluso) fino a j (esclusivo). Se i o j non sono specificati, il valore predefinito è rispettivamente 0 e la dimensione dell'elenco, ma è necessario specificare almeno i o j affinché l'intervallo sia valido.

// Allow reads of all files that begin with 'abcdef'
match /{fileName} {
  allow read: if fileName[0:6] == 'abcdef';
}

in

Restituisce true se il valore desiderato è presente nella lista o false se non presente.

// Allow read if a filename has the string 'txt' in it
match /{fileName} {
  allow read: if 'txt' in fileName.split('\\.');
}

join

Combina un elenco di stringhe in un'unica stringa, separata dalla stringa specificata.

// Allow reads if the joined array is 'file.txt'
allow read: if ['file', 'txt'].join('.') == 'file.txt';

size

Il numero di elementi nell'elenco.

// Allow read if there are three items in our list
allow read: if ['foo', 'bar', 'baz'].size() == 3;

hasAll

Restituisce true se tutti i valori sono presenti nell'elenco.

// Allow read if one list has all items in the other list
allow read: if ['file', 'txt'].hasAll(['file', 'txt']);

map

Una mappa contiene coppie chiave/valore, dove le chiavi sono stringhe e i valori possono essere uno qualsiasi tra: null , bool , int , float , string , path , list , map , timestamp o duration .

Creazione

Per creare una mappa, aggiungi coppie chiave/valore tra parentesi graffe:

// Create a map of strings to strings
{
  'mercury': 'mars',
  'rain': 'cloud',
  'cats': 'dogs',
}

Confronto

Le mappe possono essere confrontate utilizzando gli operatori == != . L'uguaglianza di due mappe richiede che tutte le chiavi siano presenti in entrambe le mappe e che tutti i valori siano uguali.

Indice

È possibile accedere ai valori in una mappa utilizzando la notazione tra parentesi o punto:

// Access custom metadata properties
allow read: if resource.metadata.property == 'property'
allow write: if resource.metadata['otherProperty'] == 'otherProperty'

Se una chiave non è presente, verrà restituito un error .

in

Restituisce true se la chiave desiderata è presente nella mappa o false se non presente.

// Allow reads if a property is present in the custom metadata
allow read: if property in resource.metadata;

size

Il numero di chiavi nella mappa.

// Allow reads if there's exactly one custom metadata key
allow read: if resource.metadata.size() == 1;

keys

Un elenco di tutte le chiavi nella mappa.

// Allow reads if the first metadata key is 'myKey'
allow read: if resource.metadata.keys()[0] == 'myKey';

values

Un elenco di tutti i valori nella mappa, in ordine chiave.

// Allow reads if the first metadata value is 'myValue'
allow read: if resource.metadata.values()[0] == 'myValue';

Errori

Valutazione degli errori

Le regole di sicurezza Firebase per Cloud Storage continuano la valutazione quando vengono rilevati errori. Questo è utile perché i condizionali && e || le espressioni possono assorbire un errore se il condizionale altrimenti andrebbe in cortocircuito rispettivamente in false o true . Ad esempio:

Espressione Risultato
error && true error
error && false false
error || true true
error || false error

I luoghi comuni in cui vengono generati errori sono: divisione per zero, accesso a valori in un elenco o mappa che non esistono e passaggio di valori di tipo errato a una funzione.

// Error if resource.size is zero
allow read: if 1000000 / resource.size;

// Error, key doesn't exist
allow read: if resource.metadata.nonExistentKey == 'value';

// Error, no unit 'y' exists
allow read: if request.time < resource.timeCreated + duration.value(1, 'y');