Controllare l'accesso a campi specifici

Questa pagina si basa sui concetti descritti in Strutturazione delle regole di sicurezza e Scrittura di condizioni per le regole di sicurezza per spiegare come puoi utilizzare Cloud Firestore Security Rules per creare regole che consentano ai client di eseguire operazioni su alcuni campi di un documento, ma non su altri.

In alcuni casi, potresti voler controllare le modifiche a un documento non a livello di documento, ma a livello di campo.

Ad esempio, potresti voler consentire a un client di creare o modificare un documento, ma non di modificare determinati campi al suo interno. In alternativa, potresti voler applicare l'obbligo che ogni documento creato da un client contenga sempre un determinato insieme di campi. Questa guida spiega come eseguire alcune di queste attività utilizzando Cloud Firestore Security Rules.

Per l'assistenza AI, copia il prompt di seguito nel tuo agente di programmazione AI (come Gemini in Android Studio, Antigravity, Claude Code, Codex e Cursor). Puoi personalizzare il prompt e ripetere l'operazione con l'agente. Visita la pagina Skill dell'agente Firebase per informazioni dettagliate sull'installazione.

Help me use Firebase in my app.
1. Make sure that **Firebase agent skills** are installed and updated. For Claude Code, Codex, or Cursor agents, help me install the Firebase plugin. For other agents, run `npx skills add firebase/agent-skills`. Always try to use **Firebase agent skills** for Firebase-related tasks.
2. Help me write, validate, and deploy Firebase Security Rules to protect my app's Firestore data.

Consentire l'accesso in lettura solo per campi specifici

Le letture nelle regole vengono eseguite a livello di documento. Puoi recuperare l'intero documento o non recuperare nulla. Non è possibile recuperare un documento parziale. È impossibile utilizzare solo le regole di sicurezza per impedire agli utenti di leggere campi specifici all'interno di un documento.

Se in un documento sono presenti determinati campi che vuoi nascondere ad alcuni utenti, il modo migliore è inserirli in un documento separato. Ad esempio, potresti prendere in considerazione la creazione di un documento in una raccolta secondaria private in questo modo:

/employees/{emp_id}

  name: "Alice Hamilton",
  department: 461,
  start_date: <timestamp>

/employees/{emp_id}/private/finances

    salary: 80000,
    bonus_mult: 1.25,
    perf_review: 4.2

Puoi quindi aggiungere regole di sicurezza con livelli di accesso diversi per le due raccolte. In questo esempio, utilizziamo le rivendicazioni di autenticazione personalizzate per indicare che solo gli utenti con la rivendicazione di autenticazione personalizzata role uguale a Finance possono visualizzare le informazioni finanziarie di un dipendente.

service cloud.firestore {
  match /databases/{database}/documents {
    // Allow any logged in user to view the public employee data
    match /employees/{emp_id} {
      allow read: if request.resource.auth != null
      // Allow only users with the custom auth claim of "Finance" to view
      // the employee's financial data
      match /private/finances {
        allow read: if request.resource.auth &&
          request.resource.auth.token.role == 'Finance'
      }
    }
  }
}

Limitazione dei campi durante la creazione dei documenti

Le regole sono prive di schema, il che significa che non esistono restrizioni a livello di database per i campi contenuti in un documento. Sebbene questa flessibilità possa semplificare lo sviluppo, in alcuni casi potresti voler assicurarti che i client possano creare solo documenti che contengono campi specifici o che non contengono altri campi.

Puoi creare queste regole esaminando il metodo keys dell'oggetto request.resource.data. Questo è un elenco di tutti i campi che il client sta tentando di scrivere in questo nuovo documento. Combinando questo insieme di campi con funzioni come hasOnly() o hasAny(), puoi aggiungere una logica che limita i tipi di documenti che un utente può aggiungere alle regole.

Richiedere campi specifici nei nuovi documenti

Supponiamo che tu voglia assicurarti che tutti i documenti creati in una raccolta restaurant contengano almeno un campo name, location e city. Puoi farlo chiamando hasAll() nell'elenco di chiavi nel nuovo documento.

service cloud.firestore {
  match /databases/{database}/documents {
    // Allow the user to create a document only if that document contains a name
    // location, and city field
    match /restaurant/{restId} {
      allow create: if request.resource.data.keys().hasAll(['name', 'location', 'city']);
    }
  }
}

In questo modo è possibile creare ristoranti anche con altri campi, ma si garantisce che tutti i documenti creati da un client contengano almeno questi tre campi.

Divieto di campi specifici nei nuovi documenti

Analogamente, puoi impedire ai client di creare documenti che contengono campi specifici utilizzando hasAny() con un elenco di campi vietati. Questo metodo restituisce true se un documento contiene uno di questi campi, quindi probabilmente vorrai negare il risultato per vietare determinati campi.

Ad esempio, nel seguente esempio, ai client non è consentito creare un documento che contenga un campo average_score o rating_count, poiché questi campi verranno aggiunti da una chiamata al server in un secondo momento.

service cloud.firestore {
  match /databases/{database}/documents {
    // Allow the user to create a document only if that document does *not*
    // contain an average_score or rating_count field.
    match /restaurant/{restId} {
      allow create: if (!request.resource.data.keys().hasAny(
        ['average_score', 'rating_count']));
    }
  }
}

Creazione di una lista consentita di campi per i nuovi documenti

Anziché vietare determinati campi nei nuovi documenti, potresti voler creare un elenco di soli campi esplicitamente consentiti nei nuovi documenti. Poi puoi utilizzare la funzione hasOnly() per assicurarti che tutti i nuovi documenti creati contengano solo questi campi (o un sottoinsieme di questi campi) e nessun altro.

service cloud.firestore {
  match /databases/{database}/documents {
    // Allow the user to create a document only if that document doesn't contain
    // any fields besides the ones listed below.
    match /restaurant/{restId} {
      allow create: if (request.resource.data.keys().hasOnly(
        ['name', 'location', 'city', 'address', 'hours', 'cuisine']));
    }
  }
}

Combinazione di campi obbligatori e facoltativi

Puoi combinare le operazioni hasAll e hasOnly nelle regole di sicurezza per richiedere alcuni campi e consentirne altri. Ad esempio, questo esempio richiede che tutti i nuovi documenti contengano i campi name, location e city e, facoltativamente, consente i campi address, hours e cuisine.

service cloud.firestore {
  match /databases/{database}/documents {
    // Allow the user to create a document only if that document has a name,
    // location, and city field, and optionally address, hours, or cuisine field
    match /restaurant/{restId} {
      allow create: if (request.resource.data.keys().hasAll(['name', 'location', 'city'])) &&
       (request.resource.data.keys().hasOnly(
           ['name', 'location', 'city', 'address', 'hours', 'cuisine']));
    }
  }
}

In uno scenario reale, potresti voler spostare questa logica in una funzione helper per evitare di duplicare il codice e combinare più facilmente i campi facoltativi e obbligatori in un unico elenco, in questo modo:

service cloud.firestore {
  match /databases/{database}/documents {
    function verifyFields(required, optional) {
      let allAllowedFields = required.concat(optional);
      return request.resource.data.keys().hasAll(required) &&
        request.resource.data.keys().hasOnly(allAllowedFields);
    }
    match /restaurant/{restId} {
      allow create: if verifyFields(['name', 'location', 'city'],
        ['address', 'hours', 'cuisine']);
    }
  }
}

Limitazione dei campi durante l'aggiornamento

Una pratica di sicurezza comune consiste nel consentire ai client di modificare solo alcuni campi e non altri. Non puoi farlo solo esaminando l'elenco request.resource.data.keys() descritto nella sezione precedente, poiché questo elenco rappresenta il documento completo come apparirebbe dopo l'aggiornamento e includerebbe quindi i campi che il client non ha modificato.

Tuttavia, se utilizzi la funzione diff(), puoi confrontare request.resource.data con l'oggetto resource.data, che rappresenta il documento nel database prima dell'aggiornamento. In questo modo viene creato un oggetto mapDiff, che contiene tutte le modifiche tra due mappe diverse.

Chiamando il metodo affectedKeys() su questo mapDiff, puoi ottenere un insieme di campi che sono stati modificati in una modifica. Puoi quindi utilizzare funzioni come hasOnly() o hasAny() per assicurarti che questo insieme contenga (o non contenga) determinati elementi.

Impedire la modifica di alcuni campi

Utilizzando il metodo hasAny() sul set generato da affectedKeys() e negando il risultato, puoi rifiutare qualsiasi richiesta del client che tenta di modificare i campi che non vuoi che vengano modificati.

Ad esempio, potresti voler consentire ai clienti di aggiornare le informazioni su un ristorante, ma non di modificare il punteggio medio o il numero di recensioni.

service cloud.firestore {
  match /databases/{database}/documents {
    match /restaurant/{restId} {
      // Allow the client to update a document only if that document doesn't
      // change the average_score or rating_count fields
      allow update: if (!request.resource.data.diff(resource.data).affectedKeys()
        .hasAny(['average_score', 'rating_count']));
    }
  }
}

Consentire la modifica solo di determinati campi

Anziché specificare i campi che non vuoi modificare, puoi anche utilizzare la funzione hasOnly() per specificare un elenco di campi che vuoi modificare. In genere, questo approccio è considerato più sicuro perché le scritture in qualsiasi nuovo campo del documento non sono consentite per impostazione predefinita finché non le consenti esplicitamente nelle regole di sicurezza.

Ad esempio, anziché non consentire i campi average_score e rating_count, puoi creare regole di sicurezza che consentano ai client di modificare solo i campi name, location, city, address, hours e cuisine.

service cloud.firestore {
  match /databases/{database}/documents {
    match /restaurant/{restId} {
    // Allow a client to update only these 6 fields in a document
      allow update: if (request.resource.data.diff(resource.data).affectedKeys()
        .hasOnly(['name', 'location', 'city', 'address', 'hours', 'cuisine']));
    }
  }
}

Ciò significa che se, in una futura iterazione della tua app, i documenti dei ristoranti includono un campo telephone, i tentativi di modificare quel campo non andranno a buon fine finché non torni indietro e aggiungi quel campo all'elenco hasOnly() nelle regole di sicurezza.

Applicazione dei tipi di campo

Un altro effetto dell'assenza di schema delle regole è che non esiste un'applicazione a livello di database per i tipi di dati che possono essere archiviati in campi specifici. Tuttavia, puoi applicare questa condizione nelle regole di sicurezza con l'operatore is.

Ad esempio, la seguente regola di sicurezza impone che il campo score di una recensione debba essere un numero intero, i campi headline, content e author_name siano stringhe e review_date sia un timestamp.

service cloud.firestore {
  match /databases/{database}/documents {
    match /restaurant/{restId} {
      // Restaurant rules go here...
      match /review/{reviewId} {
        allow create: if (request.resource.data.score is int &&
          request.resource.data.headline is string &&
          request.resource.data.content is string &&
          request.resource.data.author_name is string &&
          request.resource.data.review_date is timestamp
        );
      }
    }
  }
}

I tipi di dati validi per l'operatore is sono bool, bytes, float, int, list, latlng, number, path, map, string e timestamp. L'operatore is supporta anche i tipi di dati constraint, duration, set e map_diff, ma poiché sono generati dal linguaggio delle regole di sicurezza stesso e non dai client, raramente li utilizzi nella maggior parte delle applicazioni pratiche.

I tipi di dati list e map non supportano i tipi generici o gli argomenti di tipo. In altre parole, puoi utilizzare le regole di sicurezza per imporre che un determinato campo contenga un elenco o una mappa, ma non puoi imporre che un campo contenga un elenco di tutti i numeri interi o di tutte le stringhe.

Analogamente, puoi utilizzare le regole di sicurezza per applicare i valori dei tipi per voci specifiche in un elenco o in una mappa (utilizzando rispettivamente la notazione tra parentesi o i nomi delle chiavi), ma non esiste una scorciatoia per applicare i tipi di dati di tutti i membri in una mappa o in un elenco contemporaneamente.

Ad esempio, le seguenti regole assicurano che un campo tags in un documento contenga un elenco e che la prima voce sia una stringa. Inoltre, garantisce che il campo product contenga una mappa che a sua volta contiene un nome del prodotto che è una stringa e una quantità che è un numero intero.

service cloud.firestore {
  match /databases/{database}/documents {
  match /orders/{orderId} {
    allow create: if request.resource.data.tags is list &&
      request.resource.data.tags[0] is string &&
      request.resource.data.product is map &&
      request.resource.data.product.name is string &&
      request.resource.data.product.quantity is int
      }
    }
  }
}

I tipi di campo devono essere applicati sia durante la creazione che durante l'aggiornamento di un documento. Pertanto, ti consigliamo di creare una funzione helper che puoi chiamare sia nelle sezioni di creazione che di aggiornamento delle regole di sicurezza.

service cloud.firestore {
  match /databases/{database}/documents {

  function reviewFieldsAreValidTypes(docData) {
     return docData.score is int &&
          docData.headline is string &&
          docData.content is string &&
          docData.author_name is string &&
          docData.review_date is timestamp;
  }

   match /restaurant/{restId} {
      // Restaurant rules go here...
      match /review/{reviewId} {
        allow create: if reviewFieldsAreValidTypes(request.resource.data) &&
          // Other rules may go here
        allow update: if reviewFieldsAreValidTypes(request.resource.data) &&
          // Other rules may go here
      }
    }
  }
}

Applicazione dei tipi per i campi facoltativi

È importante ricordare che la chiamata di request.resource.data.foo su un documento in cui foo non esiste genera un errore, pertanto qualsiasi regola di sicurezza che effettua la chiamata rifiuterà la richiesta. Puoi gestire questa situazione utilizzando il metodo get su request.resource.data. Il metodo get consente di fornire un argomento predefinito per il campo che stai recuperando da una mappa se questo campo non esiste.

Ad esempio, se i documenti di revisione contengono anche un campo photo_url facoltativo e un campo tags facoltativo che vuoi verificare siano rispettivamente stringhe ed elenchi, puoi farlo riscrivendo la funzione reviewFieldsAreValidTypes in un modo simile al seguente:

  function reviewFieldsAreValidTypes(docData) {
     return docData.score is int &&
          docData.headline is string &&
          docData.content is string &&
          docData.author_name is string &&
          docData.review_date is timestamp &&
          docData.get('photo_url', '') is string &&
          docData.get('tags', []) is list;
  }

In questo modo, i documenti in cui tags esiste, ma non è un elenco, vengono rifiutati, mentre i documenti che non contengono un campo tags (o photo_url) sono comunque consentiti.

Le scritture parziali non sono mai consentite

Un'ultima nota su Cloud Firestore Security Rules: consentono al client di apportare una modifica a un documento o rifiutano l'intera modifica. Non puoi creare regole di sicurezza che accettino scritture in alcuni campi del documento e ne rifiutino altre nella stessa operazione.