Gestisci e distribuisci le regole di sicurezza di Firebase

Firebase ti fornisce diversi strumenti per gestire le tue regole, ognuno utile in casi particolari, e ognuno utilizza la stessa API di gestione delle regole di sicurezza Firebase back-end.

Indipendentemente dallo strumento utilizzato per richiamarlo, l'API di gestione:

  • Acquisisce un'origine delle regole: un insieme di regole, in genere un file di codice contenente le istruzioni delle regole di sicurezza Firebase.
  • Memorizza l'origine importata come un set di regole immutabile.
  • Tiene traccia della distribuzione di ogni set di regole in una versione . I servizi abilitati per le regole di sicurezza Firebase cercano la versione di un progetto per valutare ogni richiesta per una risorsa protetta.
  • Fornisce la capacità di eseguire test sintattici e semantici di un set di regole.

Usa l'interfaccia a riga di comando di Firebase

Con l'interfaccia a riga di comando di Firebase , puoi caricare sorgenti locali e distribuire le versioni . Firebase Local Emulator Suite della CLI consente di eseguire test locali completi delle sorgenti .

L'utilizzo dell'interfaccia a riga di comando consente di mantenere le regole sotto il controllo della versione con il codice dell'applicazione e di distribuire le regole come parte del processo di distribuzione esistente.

Genera un file di configurazione

Quando configuri il tuo progetto Firebase utilizzando l'interfaccia a riga di comando di Firebase, crei un file di configurazione .rules nella directory del progetto. Usa il comando seguente per iniziare a configurare il tuo progetto Firebase:

Cloud Firestore

// Set up Firestore in your project directory, creates a .rules file
firebase init firestore

Database in tempo reale

// Set up Realtime Database in your project directory, creates a .rules file
firebase init database

Archiviazione su cloud

// Set up Storage in your project directory, creates a .rules file
firebase init storage

Modifica e aggiorna le tue regole

Modifica l'origine delle regole direttamente nel file di configurazione .rules . Assicurati che tutte le modifiche apportate nella CLI di Firebase si riflettano nella console di Firebase o di aggiornare costantemente utilizzando la console di Firebase o la CLI di Firebase. In caso contrario, potresti sovrascrivere tutti gli aggiornamenti effettuati nella console Firebase.

Metti alla prova i tuoi aggiornamenti

La suite di emulatori locali fornisce emulatori per tutti i prodotti abilitati per le regole di sicurezza. Il motore delle regole di sicurezza per ciascun emulatore esegue la valutazione sia sintattica che semantica delle regole, superando così i test sintattici offerti dall'API di gestione delle regole di sicurezza.

Se stai lavorando con la CLI, la Suite è uno strumento eccellente per il test delle regole di sicurezza Firebase. Usa Local Emulator Suite per testare i tuoi aggiornamenti in locale e confermare che le regole della tua app mostrino il comportamento che desideri.

Distribuisci i tuoi aggiornamenti

Dopo aver aggiornato e testato le regole, distribuisci i sorgenti alla produzione. Utilizzare i seguenti comandi per distribuire selettivamente le regole da sole o distribuirle come parte del normale processo di distribuzione.

Cloud Firestore

// Deploy your .rules file
firebase deploy --only firestore:rules

Database in tempo reale

// Deploy your .rules file
firebase deploy --only database

Archiviazione su cloud

// Deploy your .rules file
firebase deploy --only storage

Usa la console Firebase

Puoi anche modificare le origini delle regole e distribuirle come versioni dalla console Firebase. Il test sintattico viene eseguito durante la modifica nell'interfaccia utente della console Firebase e il test simantico è disponibile utilizzando l'area di gioco delle regole.

Modifica e aggiorna le tue regole

  1. Apri la console Firebase e seleziona il tuo progetto.
  2. Quindi, seleziona Realtime Database , Cloud Firestore o Storage dalla navigazione del prodotto, quindi fai clic su Regole per passare all'editor delle regole.
  3. Modifica le tue regole direttamente nell'editor.

Metti alla prova i tuoi aggiornamenti

Oltre a testare la sintassi nell'interfaccia utente dell'editor, puoi testare il comportamento delle regole semantiche, utilizzando il database del tuo progetto e le risorse di archiviazione, direttamente nella console Firebase, utilizzando l'area di gioco delle regole . Apri la schermata Regole Playground nell'editor delle regole, modifica le impostazioni e fai clic su Esegui . Cerca il messaggio di conferma nella parte superiore dell'editor.

Distribuisci i tuoi aggiornamenti

Quando sei soddisfatto che gli aggiornamenti siano quelli che ti aspetti, fai clic su Pubblica .

Usa l'SDK di amministrazione

Puoi utilizzare Admin SDK per i set di regole Node.js . Con questo accesso programmatico, puoi:

  • Implementa strumenti personalizzati, script, dashboard e pipeline CI/CD per la gestione delle regole.
  • Gestisci le regole più facilmente su più progetti Firebase.

Quando si aggiornano le regole a livello di codice, è molto importante evitare di apportare modifiche indesiderate al controllo di accesso per l'app. Scrivi il codice dell'SDK di amministrazione tenendo a mente la sicurezza, soprattutto durante l'aggiornamento o la distribuzione delle regole.

Un'altra cosa importante da tenere a mente è che le versioni delle regole di sicurezza di Firebase richiedono un periodo di diversi minuti per propagarsi completamente. Quando utilizzi Admin SDK per distribuire le regole, assicurati di evitare race condition in cui la tua app si basa immediatamente su regole la cui distribuzione non è ancora completa. Se il tuo caso d'uso richiede aggiornamenti frequenti per le regole di controllo degli accessi, prendi in considerazione le soluzioni che utilizzano Cloud Firestore, progettato per ridurre le condizioni di gara nonostante i frequenti aggiornamenti.

Nota anche questi limiti:

  • Le regole devono essere inferiori a 256 KiB di testo codificato UTF-8 quando serializzate.
  • Un progetto può avere al massimo 2500 set di regole distribuiti in totale. Una volta raggiunto questo limite, è necessario eliminare alcuni vecchi set di regole prima di crearne di nuovi.

Crea e distribuisci set di regole Cloud Storage o Cloud Firestore

Un flusso di lavoro tipico per la gestione delle regole di sicurezza con Admin SDK potrebbe includere tre passaggi distinti:

  1. Crea un'origine file di regole (facoltativo)
  2. Crea un set di regole
  3. Rilascia o distribuisci il nuovo set di regole

L'SDK fornisce un metodo per combinare questi passaggi in un'unica chiamata API per le regole di sicurezza Cloud Storage e Cloud Firestore. Per esempio:

    const source = `service cloud.firestore {
      match /databases/{database}/documents {
        match /carts/{cartID} {
          allow create: if request.auth != null && request.auth.uid == request.resource.data.ownerUID;
          allow read, update, delete: if request.auth != null && request.auth.uid == resource.data.ownerUID;
        }
      }
    }`;
    // Alternatively, load rules from a file
    // const fs = require('fs');
    // const source = fs.readFileSync('path/to/firestore.rules', 'utf8');

    await admin.securityRules().releaseFirestoreRulesetFromSource(source);

Questo stesso modello funziona per le regole di Cloud Storage con releaseFirestoreRulesetFromSource() .

In alternativa, puoi creare il file delle regole come oggetto in memoria, creare il set di regole e distribuire il set di regole separatamente per un controllo più stretto di questi eventi. Per esempio:

    const rf = admin.securityRules().createRulesFileFromSource('firestore.rules', source);
    const rs = await admin.securityRules().createRuleset(rf);
    await admin.securityRules().releaseFirestoreRuleset(rs);

Aggiorna i set di regole del database in tempo reale

Per aggiornare i set di regole del database in tempo reale con Admin SDK, utilizzare i getRules() e setRules() di admin.database . Puoi recuperare i set di regole in formato JSON o come una stringa con commenti inclusi.

Per aggiornare un set di regole:

    const source = `{
      "rules": {
        "scores": {
          ".indexOn": "score",
          "$uid": {
            ".read": "$uid == auth.uid",
            ".write": "$uid == auth.uid"
          }
        }
      }
    }`;
    await admin.database().setRules(source);

Gestisci i set di regole

Per aiutare a gestire set di regole di grandi dimensioni, Admin SDK ti consente di elencare tutte le regole esistenti con admin.securityRules().listRulesetMetadata . Per esempio:

    const allRulesets = [];
    let pageToken = null;
    while (true) {
      const result = await admin.securityRules().listRulesetMetadata(pageToken: pageToken);
      allRulesets.push(...result.rulesets);
      pageToken = result.nextPageToken;
      if (!pageToken) {
        break;
      }
    }

Per distribuzioni molto grandi che raggiungono il limite di 2500 set di regole nel tempo, è possibile creare una logica per eliminare le regole meno recenti in un ciclo di tempo fisso. Ad esempio, per eliminare tutti i set di regole distribuiti per più di 30 giorni:

    const thirtyDays = new Date(Date.now() - THIRTY_DAYS_IN_MILLIS);
    const promises = [];
    allRulesets.forEach((rs) => {
      if (new Date(rs.createTime) < thirtyDays) {
        promises.push(admin.securityRules().deleteRuleset(rs.name));
      }
    });
    await Promise.all(promises);
    console.log(`Deleted ${promises.length} rulesets.`);

Usa l'API REST

Gli strumenti sopra descritti sono adatti a vari flussi di lavoro, ma potresti voler gestire e distribuire le regole di sicurezza Firebase utilizzando l'API di gestione stessa. L'API di gestione ti offre la massima flessibilità.

Tieni presente che le versioni delle regole di sicurezza Firebase richiedono un periodo di diversi minuti per propagarsi completamente. Quando utilizzi l'API REST di gestione per la distribuzione, assicurati di evitare condizioni di competizione in cui la tua app si basa immediatamente su regole la cui distribuzione non è ancora completa.

Nota anche questi limiti:

  • Le regole devono essere inferiori a 256 KiB di testo codificato UTF-8 quando serializzate.
  • Un progetto può avere al massimo 2500 set di regole distribuiti in totale. Una volta raggiunto questo limite, è necessario eliminare alcuni vecchi set di regole prima di crearne di nuovi.

Crea e distribuisci set di regole Cloud Storage o Cloud Firestore con REST

Gli esempi in questa sezione utilizzano le regole di archiviazione, sebbene si applichino anche alle regole di Cloud Firestore.

Gli esempi usano anche cURL per effettuare chiamate API. I passaggi per impostare e passare i token di autenticazione vengono omessi. Puoi sperimentare questa API utilizzando API Explorer integrato con la documentazione di riferimento .

I passaggi tipici per la creazione e la distribuzione di un set di regole utilizzando l'API di gestione sono:

  1. Creare un file di regole sorgenti
  2. Crea un set di regole
  3. Rilascia (distribuisci) il nuovo set di regole

Supponiamo che tu stia lavorando al tuo progetto Firebase secure_commerce e desideri distribuire regole di archiviazione cloud bloccate. È possibile implementare queste regole in un file storage.rules .

service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read, write: if false;
    }
  }
}

Ora, genera un'impronta digitale con codifica base64 per questo file. È quindi possibile utilizzare l'origine in questo file per popolare il payload necessario per creare un set di regole con la chiamata REST projects.rulesets.create . Qui, utilizziamo il comando cat per inserire il contenuto di storage.rules nel payload REST.

curl -X POST -d '{
  "source": {
    {
      "files": [
        {
          "content": "' $(cat storage.rules) '",
          "name": "storage.rules",
          "fingerprint": <sha fingerprint>
        }
      ]
    }
  }
}' 'https://firebaserules.googleapis.com/v1/projects/secure_commerce/rulesets'

L'API restituisce una risposta di convalida e un nome di set di regole, ad esempio projects/secure_commerce/rulesets/uuid123 . Se il set di regole è valido, il passaggio finale consiste nel distribuire il nuovo set di regole in una versione denominata.

curl -X POST -d '{
  "name": "projects/secure_commerce/releases/prod/v23   "  ,
  "rulesetName": "projects/secure_commerce/rulesets/uuid123",
}' 'https://firebaserules.googleapis.com/v1/projects/secure_commerce/releases'

Aggiorna i set di regole del database in tempo reale con REST

Realtime Database fornisce la propria interfaccia REST per la gestione delle regole. Vedere Gestione delle regole del database in tempo reale di Firebase tramite REST .

Gestisci i set di regole con REST

Per aiutare a gestire distribuzioni di regole di grandi dimensioni, oltre a un metodo REST per la creazione di set di regole e versioni, l'API di gestione fornisce metodi per:

  • elenca, ottieni ed elimina i set di regole
  • elenca, ottieni ed elimina le versioni delle regole

Per distribuzioni molto grandi che raggiungono il limite di 2500 set di regole nel tempo, è possibile creare una logica per eliminare le regole meno recenti in un ciclo di tempo fisso. Ad esempio, per eliminare tutti i set di regole distribuiti per più di 30 giorni, puoi chiamare il metodo projects.rulesets.list , analizzare l'elenco JSON degli oggetti del set di Ruleset sulle chiavi createTime , quindi chiamare project.rulesets.delete sui set di regole corrispondenti tramite ruleset_id .

Testa i tuoi aggiornamenti con REST

Infine, l'API di gestione ti consente di eseguire test sintattici e semantici sulle risorse Cloud Firestore e Cloud Storage nei tuoi progetti di produzione.

Il test con questo componente dell'API consiste in:

  1. Definizione di un oggetto JSON TestSuite per rappresentare un insieme di oggetti TestCase
  2. Invio della TestSuite
  3. L'analisi ha restituito oggetti TestResult

Definiamo un oggetto TestSuite con un singolo TestCase in un file testcase.json . In questo esempio, passiamo l'origine del linguaggio delle regole in linea con il payload REST, insieme alla suite di test per l'esecuzione su tali regole. Specifichiamo un'aspettativa di valutazione delle regole e la richiesta del client rispetto alla quale deve essere testato il set di regole. Puoi anche specificare quanto è completo il rapporto di prova, utilizzando il valore "FULL" per indicare i risultati per tutte le espressioni del linguaggio delle regole che devono essere incluse nel rapporto, comprese le espressioni che non sono state abbinate alla richiesta.

 {
  "source":
  {
    "files":
    [
      {
        "name": "firestore.rules",
        "content": "service cloud.firestore {
          match /databases/{database}/documents {
            match /users/{userId}{
              allow read: if (request.auth.uid == userId);
            }
            function doc(subpath) {
              return get(/databases/$(database)/documents/$(subpath)).data;
            }
            function isAccountOwner(accountId) {
              return request.auth.uid == accountId 
                  || doc(/users/$(request.auth.uid)).accountId == accountId;
            }
            match /licenses/{accountId} {
              allow read: if isAccountOwner(accountId);
            }
          }
        }"
      }
    ]
  },
  "testSuite":
  {
    "testCases":
    [
      {
        "expectation": "ALLOW",
        "request": {
           "auth": {"uid": "123"},
           "path": "/databases/(default)/documents/licenses/abcd",
           "method": "get"},
        "functionMocks": [
            {
            "function": "get",
            "args": [{"exact_value": "/databases/(default)/documents/users/123"}],
            "result": {"value": {"data": {"accountId": "abcd"}}}
            }
          ]
      }
    ]
  }
}

Possiamo quindi inviare questa TestSuite per la valutazione con il metodo projects.test .

curl -X POST -d '{
    ' $(cat testcase.json) '
}' 'https://firebaserules.googleapis.com/v1/projects/secure_commerce/rulesets/uuid123:test'

Il TestReport restituito (contenente lo stato SUCCESS/FAILURE del test, elenchi di messaggi di debug, elenchi di espressioni di regole visitate e relativi rapporti di valutazione) confermerebbe con lo stato SUCCESS che l'accesso è consentito correttamente.