Firebase & iOS Security

Guida Avanzata alle Firestore Security Rules per App iOS: Permessi, Ruoli e Validazione Schema

Quando si sviluppa un'applicazione iOS nativa che utilizza Cloud Firestore come database NoSQL serverless, la sicurezza dei dati non risiede sul dispositivo client, ma è affidata interamente alle Firestore Security Rules. Lasciare le regole in modalità di test o utilizzare verifiche generiche (come allow read, write: if request.auth != null;) espone l'applicazione a rischi gravissimi di sottrazione o alterazione dei dati.

1. Il Principio del Minimo Privilegio

Le Security Rules di Firestore funzionano su base deny-by-default: se nessuna regola autorizza esplicitamente un'operazione, la richiesta viene bloccata. È fondamentale separare i permessi di lettura in get (lettura singolo documento) e list (query su collezioni), e i permessi di scrittura in create, update e delete.

Esempio Pratico di Security Rules Produzione

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {

    // Helper functions
    function isAuthenticated() {
      return request.auth != null;
    }
    
    function isOwner(userId) {
      return isAuthenticated() && request.auth.uid == userId;
    }
    
    function hasRole(role) {
      return isAuthenticated() && request.auth.token.role == role;
    }

    // Regole per la collezione degli utenti
    match /users/{userId} {
      allow get: if isAuthenticated();
      allow list: if hasRole('admin');
      allow create: if isOwner(userId) 
        && request.resource.data.keys().hasAll(['email', 'createdAt'])
        && request.resource.data.email is string;
      allow update: if isOwner(userId) 
        && !request.resource.data.diff(resource.data).affectedKeys().hasAny(['role', 'createdAt']);
      allow delete: if hasRole('admin');
    }

    // Regole per post e contenuti
    match /posts/{postId} {
      allow read: if true; // Pubblico in lettura
      allow create: if isAuthenticated() 
        && request.resource.data.authorId == request.auth.uid
        && request.resource.data.title.size() >= 3;
      allow update, delete: if isAuthenticated() 
        && resource.data.authorId == request.auth.uid;
    }
  }
}

2. Integrazione nel Client iOS (SwiftUI & Swift)

Lato client in SwiftUI, è necessario intercettare e gestire gli errori di permesso negato generati da Firebase per mostrare messaggi chiari all'utente o eseguire il rollback dello stato locale.

import SwiftUI
import FirebaseFirestore
import FirebaseAuth

final class PostViewModel: ObservableObject {
    @Published var posts: [Post] = []
    @Published var errorMessage: String?
    
    private let db = Firestore.firestore()
    
    func createPost(title: String, content: String) {
        guard let currentUid = Auth.auth().currentUser?.uid else {
            self.errorMessage = "Utente non autenticato."
            return
        }
        
        let newPost: [String: Any] = [
            "title": title,
            "content": content,
            "authorId": currentUid,
            "createdAt": FieldValue.serverTimestamp()
        ]
        
        db.collection("posts").addDocument(data: newPost) { error in
            if let error = error as NSError? {
                if error.code == FirestoreErrorCode.permissionDenied.rawValue {
                    self.errorMessage = "Non disponi dei permessi per pubblicare questo post."
                } else {
                    self.errorMessage = error.localizedDescription
                }
            }
        }
    }
}

Conclusioni

Implementare le Firestore Security Rules con validazione dello schema e verifica dei token garantisce che il tuo database sia corazzato contro qualsiasi abuso. Per consulenze architetturali su Firebase ed iOS, contatta Yunus Diallo (DialloDev) a diallooyunus@gmail.com.