Korzystanie z warunków w regułach zabezpieczeń Cloud Storage Firebase

Ten przewodnik jest rozszerzeniem przewodnika dotyczącego podstawowej składni języka Firebase Security Rules Pokazuje, jak dodawać warunki do Firebase Security Rules dla Cloud Storage.

Podstawowym elementem składowym Cloud Storage Security Rules jest warunek. Warunek to wyrażenie logiczne, które określa, czy dana operacja powinna być dozwolona czy zabroniona. W przypadku podstawowych reguł używanie literałów true i false jako warunków sprawdza się doskonale. Język Firebase Security Rules dla Cloud Storage umożliwia pisanie bardziej złożonych warunków, które mogą:

  • sprawdzać uwierzytelnianie użytkowników,
  • weryfikować przychodzące dane.

Uwierzytelnianie

Firebase Security Rules dla Cloud Storage są zintegrowane z Firebase Authentication, aby zapewnić zaawansowane uwierzytelnianie użytkowników w Cloud Storage. Umożliwia to precyzyjną kontrolę dostępu na podstawie deklaracji tokena Firebase Authentication.

Gdy uwierzytelniony użytkownik wysyła żądanie do Cloud Storage, zmienna request.auth jest wypełniana uid (request.auth.uid) użytkownika oraz deklaracjami tokena JWT Firebase Authentication (request.auth.token).

Dodatkowo, gdy używasz uwierzytelniania niestandardowego, dodatkowe deklaracje są widoczne w polu request.auth.token.

Gdy żądanie wysyła nieuwierzytelniony użytkownik, zmienna request.auth ma wartość null.

Na podstawie tych danych można zabezpieczać pliki na kilka typowych sposobów:

  • Publiczne: ignoruj request.auth.
  • Prywatne z uwierzytelnianiem: sprawdź, czy request.auth nie ma wartości null.
  • Prywatne dla użytkownika: sprawdź, czy request.auth.uid jest równe uid ścieżki.
  • Prywatne dla grupy: sprawdź deklaracje tokena niestandardowego, aby dopasować je do wybranej deklaracji, lub odczytaj metadane pliku, aby sprawdzić, czy istnieje pole metadanych.

Publiczne

Każda reguła, która nie uwzględnia kontekstu request.auth, może być uznawana za public regułę, ponieważ nie uwzględnia kontekstu uwierzytelniania użytkownika. Te reguły mogą być przydatne do udostępniania danych publicznych, takich jak zasoby gry, pliki dźwiękowe lub inne treści statyczne.

// Anyone to read a public image if the file is less than 100kB
// Anyone can upload a public file ending in '.txt'
match /public/{imageId} {
  allow read: if resource.size < 100 * 1024;
  allow write: if imageId.matches(".*\\.txt");
}

Prywatne z uwierzytelnianiem

W niektórych przypadkach możesz chcieć, aby dane były widoczne dla wszystkich uwierzytelnionych użytkowników aplikacji, ale nie dla użytkowników nieuwierzytelnionych. Ponieważ zmienna request.auth ma wartość null dla wszystkich nieuwierzytelnionych użytkowników, wystarczy sprawdzić, czy zmienna request.auth istnieje, aby wymagać uwierzytelnienia:

// Require authentication on all internal image reads
match /internal/{imageId} {
  allow read: if request.auth != null;
}

Prywatne dla użytkownika

Najczęstszym przypadkiem użycia request.auth jest przyznawanie poszczególnym użytkownikom szczegółowych uprawnień do ich plików: od przesyłania zdjęć profilowych po odczytywanie dokumentów prywatnych.

Ponieważ pliki w Cloud Storage mają pełną "ścieżkę" do pliku, wystarczy, aby plik był kontrolowany przez użytkownika, jeśli w prefiksie nazwy pliku znajduje się unikalna informacja identyfikująca użytkownika (np. uid użytkownika), którą można sprawdzić podczas oceny reguły:

// Only a user can upload their profile picture, but anyone can view it
match /users/{userId}/profilePicture.png {
  allow read;
  allow write: if request.auth.uid == userId;
}

Prywatne dla grupy

Innym równie częstym przypadkiem użycia jest zezwalanie na uprawnienia grupowe do obiektu, np. umożliwianie kilku członkom zespołu współpracy nad udostępnionym dokumentem. Można to zrobić na kilka sposobów:

  • Wygeneruj Firebase Authentication token niestandardowy który zawiera dodatkowe informacje o członku grupy (np. identyfikator grupy).
  • Dołącz informacje o grupie (np. identyfikator grupy lub listę autoryzowanych uids) do metadanych pliku

Gdy te dane są przechowywane w tokenie lub metadanych pliku, można się do nich odwoływać w regule:

// Allow reads if the group ID in your token matches the file metadata's `owner` property
// Allow writes if the group ID is in the user's custom token
match /files/{groupId}/{fileName} {
  allow read: if resource.metadata.owner == request.auth.token.groupId;
  allow write: if request.auth.token.groupId == groupId;
}

Ocena żądania

Przesyłanie, pobieranie, zmiany metadanych i usuwanie są oceniane za pomocą request wysyłanego do Cloud Storage. Oprócz unikalnego identyfikatora użytkownika i ładunku Firebase Authentication w obiekcie request.auth (jak opisano powyżej) zmienna request zawiera ścieżkę pliku, w którym jest wykonywane żądanie, czas otrzymania żądania oraz nową wartość resource jeśli żądanie jest zapisem.

Obiekt request zawiera też unikalny identyfikator użytkownika i Firebase Authentication ładunek w obiekcie request.auth, co zostanie dokładniej wyjaśnione w sekcji zabezpieczeń opartych na użytkownikach w dokumentacji.

Pełną listę właściwości obiektu request znajdziesz poniżej:

Właściwość Typ Opis
auth map<string, string> Gdy użytkownik jest zalogowany, udostępnia uid, unikalny identyfikator użytkownika, oraz token, mapę deklaracji tokena JWT Firebase Authentication. W przeciwnym razie będzie mieć wartość null.
params map<string, string> Mapa zawierająca parametry zapytania żądania.
path path A path reprezentująca ścieżkę, w której jest wykonywane żądanie.
resource map<string, string> Nowa wartość zasobu, dostępna tylko w żądaniach write.
time timestamp Sygnatura czasowa reprezentująca czas serwera, w którym jest oceniane żądanie.

Ocena zasobów

Podczas oceniania reguł możesz też oceniać metadane przesyłanego, pobieranego, modyfikowanego lub usuwanego pliku. Umożliwia to tworzenie złożonych i zaawansowanych reguł, które np. zezwalają na przesyłanie tylko plików o określonych typach treści lub usuwanie tylko plików o określonym rozmiarze.

Firebase Security Rules dla Cloud Storage udostępnia metadane pliku w obiekcie resource, który zawiera pary klucz/wartość metadanych widoczne w obiekcie Cloud Storage. Te właściwości można sprawdzać w żądaniach read lub write, aby zapewnić integralność danych.

W przypadku żądań write (takich jak przesyłanie, aktualizowanie metadanych i usuwanie) oprócz obiektu resource, który zawiera metadane pliku znajdującego się obecnie w ścieżce żądania, możesz też używać obiektu request.resource, który zawiera podzbiór metadanych pliku, które mają zostać zapisane, jeśli zapis jest dozwolony. Możesz używać tych 2 wartości, aby zapewnić integralność danych lub wymusić ograniczenia aplikacji, takie jak typ lub rozmiar pliku.

Pełną listę właściwości obiektu resource znajdziesz poniżej:

Właściwość Typ Opis
name string Pełna nazwa obiektu.
bucket string Nazwa zasobnika, w którym znajduje się ten obiekt.
generation int Generacja obiektu Google Cloud Storage obiektu.
metageneration int Metageneracja obiektu Google Cloud Storage.
size int Rozmiar obiektu w bajtach.
timeCreated timestamp Sygnatura czasowa reprezentująca czas utworzenia obiektu.
updated timestamp Sygnatura czasowa reprezentująca czas ostatniej aktualizacji obiektu.
md5Hash string Skrót MD5 obiektu.
crc32c string Skrót crc32c obiektu.
etag string Tag etag powiązany z tym obiektem.
contentDisposition string Dyspozycja treści powiązana z tym obiektem.
contentEncoding string Kodowanie treści powiązane z tym obiektem.
contentLanguage string Język treści powiązany z tym obiektem.
contentType string Typ treści powiązany z tym obiektem.
metadata map<string, string> Pary klucz/wartość dodatkowych metadanych niestandardowych określonych przez dewelopera.

request.resource zawiera wszystkie te elementy z wyjątkiem generation, metageneration, etag, timeCreated i updated.

Ulepszanie za pomocą Cloud Firestore

Aby ocenić inne kryteria autoryzacji, możesz uzyskać dostęp do dokumentów w Cloud Firestore.

Za pomocą funkcji firestore.get() i firestore.exists() reguły bezpieczeństwa mogą oceniać przychodzące żądania w porównaniu z dokumentami w Cloud Firestore. Funkcje firestore.get() i firestore.exists() oczekują w pełni określonych ścieżek dokumentów. Gdy używasz zmiennych do tworzenia ścieżek dla firestore.get() i firestore.exists(), musisz jawnie wskazać zmianę znaczenia zmiennych za pomocą składni $(variable).

W przykładzie poniżej widzimy regułę, która ogranicza dostęp do odczytu plików do tych użytkowników, którzy są członkami określonych klubów.

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.id)).data.memberships
    }
  }
}
W następnym przykładzie tylko znajomi użytkownika mogą zobaczyć jego zdjęcia.
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.id))
    }
  }
}

Gdy utworzysz i zapiszesz pierwsze Cloud Storage Security Rules, które używają tych Cloud Firestore funkcji, w konsoli Firebase lub interfejsie wiersza poleceń Firebase pojawi się prośba o włączenie uprawnień do połączenia tych 2 usług.

Możesz wyłączyć tę funkcję, usuwając rolę IAM, jak opisano w Zarządzanie regułami bezpieczeństwa Firebase i ich wdrażanieFirebase Security Rules.

Sprawdzanie danych

Firebase Security Rules dla Cloud Storage mogą też służyć do sprawdzania danych, w tym nazwy i ścieżki pliku oraz właściwości metadanych pliku, takich jak contentType i size.

service firebase.storage {
  match /b/{bucket}/o {
    match /images/{imageId} {
      // Only allow uploads of any image file that's less than 5MB
      allow write: if request.resource.size < 5 * 1024 * 1024
                   && request.resource.contentType.matches('image/.*');
    }
  }
}

Funkcje niestandardowe

Gdy Twoje Firebase Security Rules stają się bardziej złożone, możesz chcieć umieścić zestawy warunków w funkcjach, których możesz używać w całym zestawie reguł. Reguły bezpieczeństwa obsługują funkcje niestandardowe. Składnia funkcji niestandardowych jest podobna do JavaScript, ale Firebase Security Rules funkcje są pisane w języku specyficznym dla domeny który ma pewne ważne ograniczenia:

  • Funkcje mogą zawierać tylko jedną instrukcję return. Nie mogą zawierać żadnej dodatkowej logiki. Nie mogą na przykład wykonywać pętli ani wywoływać usług zewnętrznych.
  • Funkcje mogą automatycznie uzyskiwać dostęp do funkcji i zmiennych z zakresu, w którym są zdefiniowane. Na przykład funkcja zdefiniowana w zakresie service firebase.storage ma dostęp do zmiennej resource, a w przypadku Cloud Firestore – tylko do wbudowanych funkcji takich jak get() i exists().
  • Funkcje mogą wywoływać inne funkcje, ale nie mogą być rekurencyjne. Łączna głębokość stosu wywołań jest ograniczona do 10.
  • W wersji rules2 funkcje mogą definiować zmienne za pomocą słowa kluczowego let. Funkcje mogą mieć dowolną liczbę powiązań let, ale muszą kończyć się instrukcją return.

Funkcja jest definiowana za pomocą słowa kluczowego function i przyjmuje 0 lub więcej argumentów. Możesz na przykład połączyć 2 typy warunków używane w powyższych przykładach w jedną funkcję:

service firebase.storage {
  match /b/{bucket}/o {
    // True if the user is signed in or the requested data is 'public'
    function signedInOrPublic() {
      return request.auth.uid != null || resource.data.visibility == 'public';
    }
    match /images/{imageId} {
      allow read, write: if signedInOrPublic();
    }
    match /mp3s/{mp3Ids} {
      allow read: if signedInOrPublic();
    }
  }
}

Używanie funkcji w Firebase Security Rules ułatwia ich utrzymanie w miarę wzrostu złożoności reguł.

Dalsze kroki

Po omówieniu warunków masz bardziej zaawansowaną wiedzę o regułach i możesz:

Dowiedzieć się, jak obsługiwać podstawowe przypadki użycia, oraz poznać proces tworzenia, testowania i wdrażania reguł: