Catch up on everything announced at Firebase Summit, and learn how Firebase can help you accelerate app development and run your app with confidence. Learn More

Как работают правила безопасности

Оптимизируйте свои подборки Сохраняйте и классифицируйте контент в соответствии со своими настройками.

Безопасность может быть одной из самых сложных частей головоломки разработки приложений. В большинстве приложений разработчики должны создать и запустить сервер, который обрабатывает аутентификацию (кто такой пользователь) и авторизацию (что пользователь может делать).

Правила безопасности Firebase удаляют средний (серверный) уровень и позволяют вам указывать разрешения на основе пути для клиентов, которые подключаются к вашим данным напрямую. Используйте это руководство, чтобы узнать больше о том, как правила применяются к входящим запросам.

Выберите продукт, чтобы узнать больше о его правилах.

Облако Firestore

Базовая структура

Правила безопасности Firebase в Cloud Firestore и Cloud Storage используют следующую структуру и синтаксис:

service <<name>> {
  // Match the resource path.
  match <<path>> {
    // Allow the request if the following conditions are true.
    allow <<methods>> : if <<condition>>
  }
}

При построении правил важно понимать следующие ключевые понятия:

  • Запрос: метод или методы, вызываемые в операторе allow . Это методы, которые вы разрешаете запускать. Стандартные методы: get , list , create , update и delete . Удобные методы read и write обеспечивают широкий доступ для чтения и записи к указанной базе данных или пути к хранилищу.
  • Путь: база данных или место хранения, представленное в виде пути URI.
  • Правило: оператор allow , который включает условие, разрешающее запрос, если оно оценивается как истинное.

Правила безопасности версии 2

По состоянию на май 2019 года доступна версия 2 правил безопасности Firebase. Версия 2 правил изменяет поведение рекурсивных подстановочных знаков {name=**} . Вы должны использовать версию 2, если планируете использовать запросы группы сбора . Вы должны подписаться на версию 2, rules_version = '2'; первая строка в ваших правилах безопасности:

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

Соответствующие пути

Все операторы сопоставления должны указывать на документы, а не на коллекции. Оператор match может указывать на конкретный документ, как в match /cities/SF , или использовать подстановочные знаки для указания на любой документ по указанному пути, как в match /cities/{city} .

В приведенном выше примере оператор match использует синтаксис подстановочного знака {city} . Это означает, что правило применяется к любому документу в коллекции cities , например /cities/SF или /cities/NYC . Когда выражения allow в операторе соответствия оцениваются, переменная city преобразуется в имя документа города, например SF или NYC .

Соответствующие подколлекции

Данные в Cloud Firestore организованы в коллекции документов, и каждый документ может расширять иерархию за счет вложенных коллекций. Важно понимать, как правила безопасности взаимодействуют с иерархическими данными.

Рассмотрим ситуацию, когда каждый документ в коллекции cities содержит подколлекцию landmarks . Правила безопасности применяются только к совпадающему пути, поэтому элементы управления доступом, определенные для коллекции cities , не применяются к подколлекции landmarks . Вместо этого напишите явные правила для управления доступом к подколлекциям:

service cloud.firestore {
  match /databases/{database}/documents {
    match /cities/{city} {
      allow read, write: if <condition>;

      // Explicitly define rules for the 'landmarks' subcollection
      match /landmarks/{landmark} {
        allow read, write: if <condition>;
      }
    }
  }
}

При вложении операторов match путь внутреннего оператора match всегда относится к пути внешнего оператора match . Таким образом, следующие наборы правил эквивалентны:

service cloud.firestore {
  match /databases/{database}/documents {
    match /cities/{city} {
      match /landmarks/{landmark} {
        allow read, write: if <condition>;
      }
    }
  }
}
service cloud.firestore {
  match /databases/{database}/documents {
    match /cities/{city}/landmarks/{landmark} {
      allow read, write: if <condition>;
    }
  }
}

Рекурсивные подстановочные знаки

Если вы хотите, чтобы правила применялись к произвольно глубокой иерархии, используйте синтаксис рекурсивных подстановочных знаков, {name=**} :

service cloud.firestore {
  match /databases/{database}/documents {
    // Matches any document in the cities collection as well as any document
    // in a subcollection.
    match /cities/{document=**} {
      allow read, write: if <condition>;
    }
  }
}

При использовании рекурсивного синтаксиса подстановочных знаков переменная подстановочных знаков будет содержать весь соответствующий сегмент пути, даже если документ находится в глубоко вложенной подколлекции. Например, перечисленные выше правила будут соответствовать документу, расположенному в /cities/SF/landmarks/coit_tower , а значение переменной document будет SF/landmarks/coit_tower .

Обратите внимание, однако, что поведение рекурсивных подстановочных знаков зависит от версии правил.

Версия 1

Правила безопасности используют версию 1 по умолчанию. В версии 1 рекурсивные подстановочные знаки соответствуют одному или нескольким элементам пути. Они не соответствуют пустому пути, поэтому match /cities/{city}/{document=**} соответствует документам в подколлекциях, но не в коллекции cities , тогда как match /cities/{document=**} соответствует обоим документам в коллекции. коллекции и подколлекции cities .

Рекурсивные подстановочные знаки должны стоять в конце оператора соответствия.

Версия 2

В версии 2 правил безопасности рекурсивные подстановочные знаки соответствуют нулю или более элементам пути. match/cities/{city}/{document=**} соответствует документам в любых вложенных коллекциях, а также документам в коллекции cities .

Вы должны подписаться на версию 2, добавив rules_version = '2'; в верхней части ваших правил безопасности:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    // Matches any document in the cities collection as well as any document
    // in a subcollection.
    match /cities/{city}/{document=**} {
      allow read, write: if <condition>;
    }
  }
}

У вас может быть не более одного рекурсивного подстановочного знака на оператор соответствия, но в версии 2 вы можете разместить этот подстановочный знак в любом месте оператора соответствия. Например:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    // Matches any document in the songs collection group
    match /{path=**}/songs/{song} {
      allow read, write: if <condition>;
    }
  }
}

Если вы используете запросы группы сбора , вы должны использовать версию 2, см. раздел Защита запросов группы сбора .

Перекрывающиеся операторы соответствия

Документ может соответствовать более чем одному оператору match . В true , когда запросу соответствует несколько allow выражений, доступ разрешается, если выполняется любое из условий:

service cloud.firestore {
  match /databases/{database}/documents {
    // Matches any document in the 'cities' collection.
    match /cities/{city} {
      allow read, write: if false;
    }

    // Matches any document in the 'cities' collection or subcollections.
    match /cities/{document=**} {
      allow read, write: if true;
    }
  }
}

В приведенном выше примере будут разрешены все операции чтения и записи в коллекцию cities , поскольку второе правило всегда true , хотя первое правило всегда false .

Ограничения правил безопасности

При работе с правилами безопасности обратите внимание на следующие ограничения:

Ограничение Подробности
Максимальное количество вызовов exists() , get() и getAfter() на запрос
  • 10 для запросов отдельных документов и запросов запросов.
  • 20 для чтения нескольких документов, транзакций и пакетной записи. Предыдущее ограничение в 10 также применяется к каждой операции.

    Например, представьте, что вы создаете пакетный запрос на запись с 3 операциями записи и что ваши правила безопасности используют 2 вызова доступа к документу для проверки каждой записи. В этом случае каждая запись использует 2 из 10 вызовов доступа, а пакетный запрос на запись использует 6 из 20 вызовов доступа.

Превышение любого ограничения приводит к ошибке отказа в разрешении.

Некоторые вызовы доступа к документам могут кэшироваться, а кэшированные вызовы не учитываются при расчете ограничений.

Максимальная глубина вложенного оператора match 10
Максимальная длина пути в сегментах пути, допустимая в наборе вложенных операторов match . 100
Максимальное количество переменных захвата пути, разрешенное в наборе вложенных операторов match 20
Максимальная глубина вызова функции 20
Максимальное количество аргументов функции 7
Максимальное количество привязок переменной let на функцию 10
Максимальное количество рекурсивных или циклических вызовов функций 0 (не разрешено)
Максимальное количество выражений, оцениваемых на запрос 1000
Максимальный размер набора правил Наборы правил должны соответствовать двум ограничениям размера:
  • ограничение в 256 КБ на размер исходного текста набора правил, опубликованного из консоли Firebase или из интерфейса командной строки с помощью firebase deploy .
  • ограничение в 250 КБ на размер скомпилированного набора правил, которое возникает, когда Firebase обрабатывает исходный код и делает его активным на серверной части.

Облачное хранилище

Базовая структура

Правила безопасности Firebase в Cloud Firestore и Cloud Storage используют следующую структуру и синтаксис:

service <<name>> {
  // Match the resource path.
  match <<path>> {
    // Allow the request if the following conditions are true.
    allow <<methods>> : if <<condition>>
  }
}

При построении правил важно понимать следующие ключевые понятия:

  • Запрос: метод или методы, вызываемые в операторе allow . Это методы, которые вы разрешаете запускать. Стандартные методы: get , list , create , update и delete . Удобные методы read и write обеспечивают широкий доступ для чтения и записи к указанной базе данных или пути к хранилищу.
  • Путь: база данных или место хранения, представленное в виде пути URI.
  • Правило: оператор allow , который включает условие, разрешающее запрос, если оно оценивается как истинное.

Соответствующие пути

Правила безопасности облачного хранилища match путям к файлам, используемым для доступа к файлам в облачном хранилище. Правила могут match точным путям или путям с подстановочными знаками, а также правила могут быть вложенными. Если ни одно правило соответствия не разрешает метод запроса или условие оценивается как false , запрос отклоняется.

Точные совпадения

// Exact match for "images/profilePhoto.png"
match /images/profilePhoto.png {
  allow write: if <condition>;
}

// Exact match for "images/croppedProfilePhoto.png"
match /images/croppedProfilePhoto.png {
  allow write: if <other_condition>;
}

Вложенные совпадения

// Partial match for files that start with "images"
match /images {
  // Exact match for "images/profilePhoto.png"
  match /profilePhoto.png {
    allow write: if <condition>;
  }

  // Exact match for "images/croppedProfilePhoto.png"
  match /croppedProfilePhoto.png {
    allow write: if <other_condition>;
  }
}

Совпадения с подстановочными знаками

Правила также можно использовать для match с шаблоном с помощью подстановочных знаков. Подстановочный знак — это именованная переменная, представляющая либо одну строку, например profilePhoto.png , либо несколько сегментов пути, например images/profilePhoto.png .

Подстановочный знак создается путем добавления фигурных скобок вокруг имени подстановочного знака, например {string} . Подстановочный знак для нескольких сегментов можно объявить, добавив =** к имени подстановочного знака, например {path=**} :

// Partial match for files that start with "images"
match /images {
  // Exact match for "images/*"
  // e.g. images/profilePhoto.png is matched
  match /{imageId} {
    // This rule only matches a single path segment (*)
    // imageId is a string that contains the specific segment matched
    allow read: if <condition>;
  }

  // Exact match for "images/**"
  // e.g. images/users/user:12345/profilePhoto.png is matched
  // images/profilePhoto.png is also matched!
  match /{allImages=**} {
    // This rule matches one or more path segments (**)
    // allImages is a path that contains all segments matched
    allow read: if <other_condition>;
  }
}

Если несколько правил соответствуют файлу, результатом является OR результата оценки всех правил. То есть, если какое-либо правило, которому соответствует файл, оценивается как true , результат будет true .

В приведенных выше правилах файл «images/profilePhoto.png» может быть прочитан, если condition или other_condition оценивается как истинное, в то время как файл «images/users/user:12345/profilePhoto.png» зависит только от результата other_condition .

На переменную с подстановочными знаками можно ссылаться из-за match имени файла или авторизации пути:

// Another way to restrict the name of a file
match /images/{imageId} {
  allow read: if imageId == "profilePhoto.png";
}

Правила безопасности облачного хранилища не каскадируются, и правила оцениваются только тогда, когда путь запроса совпадает с путем с указанными правилами.

Запросить оценку

Загрузки, загрузки, изменения метаданных и удаления оцениваются с использованием request , отправленного в облачное хранилище. Переменная request содержит путь к файлу, в котором выполняется запрос, время получения запроса и новое значение resource , если запрос является записью. Также включены заголовки HTTP и состояние аутентификации.

Объект request также содержит уникальный идентификатор пользователя и полезную нагрузку аутентификации Firebase в объекте request.auth , что будет объяснено далее в разделе документации по аутентификации .

Полный список свойств в объекте request доступен ниже:

Имущество Тип Описание
auth карта<строка, строка> Когда пользователь входит в систему, предоставляет uid , уникальный идентификатор пользователя и token , карту утверждений Firebase Authentication JWT. В противном случае он будет null .
params карта<строка, строка> Карта, содержащая параметры запроса запроса.
path дорожка path , представляющий путь, по которому выполняется запрос.
resource карта<строка, строка> Новое значение ресурса, присутствующее только в запросах на write .
time отметка времени Временная метка, представляющая время сервера, в которое оценивается запрос.

Оценка ресурсов

При оценке правил вы также можете оценить метаданные загружаемого, загружаемого, изменяемого или удаляемого файла. Это позволяет создавать сложные и мощные правила, которые разрешают загрузку только файлов с определенным типом содержимого или удаление только файлов, превышающих определенный размер.

Правила безопасности Firebase для облачного хранилища предоставляют метаданные файла в объекте resource , который содержит пары «ключ-значение» метаданных, отображаемых в объекте облачного хранилища. Эти свойства можно проверять при запросах на read или write для обеспечения целостности данных.

В запросах на write (таких как загрузка, обновление метаданных и удаление) в дополнение к объекту resource , который содержит метаданные файла для файла, который в настоящее время существует по пути запроса, у вас также есть возможность использовать объект request.resource , который содержит подмножество метаданных файла для записи, если запись разрешена. Эти два значения можно использовать для обеспечения целостности данных или принудительного применения ограничений приложения, таких как тип или размер файла.

Полный список свойств в resource объекте доступен ниже:

Имущество Тип Описание
name нить Полное название объекта
bucket нить Имя корзины, в которой находится этот объект.
generation инт Создание объекта Google Cloud Storage для этого объекта.
metageneration инт Метагенерация объекта Google Cloud Storage этого объекта.
size инт Размер объекта в байтах.
timeCreated отметка времени Отметка времени, представляющая время создания объекта.
updated отметка времени Отметка времени, представляющая время последнего обновления объекта.
md5Hash нить Хэш MD5 объекта.
crc32c нить Хэш crc32c объекта.
etag нить Тег, связанный с этим объектом.
contentDisposition нить Расположение содержимого, связанное с этим объектом.
contentEncoding нить Кодировка содержимого, связанная с этим объектом.
contentLanguage нить Язык содержимого, связанный с этим объектом.
contentType нить Тип содержимого, связанный с этим объектом.
metadata карта<строка, строка> Пары ключ/значение дополнительных настраиваемых метаданных, указанных разработчиком.

request.resource содержит все это, за исключением generation , metageneration , etag , timeCreated и updated .

Ограничения правил безопасности

При работе с правилами безопасности обратите внимание на следующие ограничения:

Ограничение Подробности
Максимальное количество вызовов firestore.exists() и firestore.get() на запрос

2 для запросов отдельных документов и запросов запросов.

Превышение этого ограничения приводит к ошибке отказа в доступе.

Вызовы доступа к одним и тем же документам могут кэшироваться, а кэшированные вызовы не учитываются при расчете ограничений.

Полный пример

Собрав все вместе, вы можете создать полный пример правил для решения для хранения изображений:

service firebase.storage {
 match /b/{bucket}/o {
   match /images {
     // Cascade read to any image type at any path
     match /{allImages=**} {
       allow read;
     }

     // Allow write files to the path "images/*", subject to the constraints:
     // 1) File is less than 5MB
     // 2) Content type is an image
     // 3) Uploaded content type matches existing content type
     // 4) File name (stored in imageId wildcard variable) is less than 32 characters
     match /{imageId} {
       allow write: if request.resource.size < 5 * 1024 * 1024
                    && request.resource.contentType.matches('image/.*')
                    && request.resource.contentType == resource.contentType
                    && imageId.size() < 32
     }
   }
 }
}

База данных реального времени

Базовая структура

В базе данных реального времени правила безопасности Firebase состоят из выражений, подобных JavaScript, содержащихся в документе JSON.

Они используют следующий синтаксис:

{
  "rules": {
    "<<path>>": {
    // Allow the request if the condition for each method is true.
      ".read": <<condition>>,
      ".write": <<condition>>,
      ".validate": <<condition>>
    }
  }
}

В правиле есть три основных элемента:

  • Путь: расположение базы данных. Это отражает структуру JSON вашей базы данных.
  • Запрос: это методы, которые правило использует для предоставления доступа. Правила read и write предоставляют широкий доступ для чтения и записи, а правила validate действуют как вторичная проверка для предоставления доступа на основе входящих или существующих данных.
  • Условие: условие, которое разрешает запрос, если оно оценивается как истинное.

Как правила применяются к путям

В базе данных реального времени правила применяются атомарно, что означает, что правила на родительских узлах более высокого уровня переопределяют правила на более детализированных дочерних узлах, а правила на более глубоком узле не могут предоставлять доступ к родительскому пути. Вы не можете уточнить или отозвать доступ к более глубокому пути в структуре базы данных, если вы уже предоставили его для одного из родительских путей.

Учтите следующие правила:

{
  "rules": {
     "foo": {
        // allows read to /foo/*
        ".read": "data.child('baz').val() === true",
        "bar": {
          // ignored, since read was allowed already
          ".read": false
        }
     }
  }
}

Эта структура безопасности позволяет читать /bar/ всякий раз, когда /foo/ содержит дочерний baz со значением true . ".read": false в /foo/bar/ здесь не действует, так как доступ не может быть отозван дочерним путем.

Хотя это может показаться не сразу интуитивно понятным, это мощная часть языка правил, которая позволяет реализовать очень сложные привилегии доступа с минимальными усилиями. Это особенно полезно для обеспечения безопасности на основе пользователей .

Однако правила .validate не каскадируются. Все правила проверки должны выполняться на всех уровнях иерархии, чтобы запись была разрешена.

Кроме того, поскольку правила не применяются обратно к родительскому пути, операция чтения или записи завершится ошибкой, если в запрошенном расположении или в родительском расположении нет правила, предоставляющего доступ. Даже если каждый затронутый дочерний путь доступен, чтение в родительском местоположении полностью завершится ошибкой. Рассмотрим эту структуру:

{
  "rules": {
    "records": {
      "rec1": {
        ".read": true
      },
      "rec2": {
        ".read": false
      }
    }
  }
}

Без понимания того, что правила оцениваются атомарно, может показаться, что выборка пути /records/ вернет rec1 но не rec2 . Фактический результат, однако, является ошибкой:

JavaScript
var db = firebase.database();
db.ref("records").once("value", function(snap) {
  // success method is not called
}, function(err) {
  // error callback triggered with PERMISSION_DENIED
});
Цель-C
Примечание. Этот продукт Firebase недоступен для цели App Clip.
FIRDatabaseReference *ref = [[FIRDatabase database] reference];
[[_ref child:@"records"] observeSingleEventOfType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) {
  // success block is not called
} withCancelBlock:^(NSError * _Nonnull error) {
  // cancel block triggered with PERMISSION_DENIED
}];
Быстрый
Примечание. Этот продукт Firebase недоступен для цели App Clip.
var ref = FIRDatabase.database().reference()
ref.child("records").observeSingleEventOfType(.Value, withBlock: { snapshot in
    // success block is not called
}, withCancelBlock: { error in
    // cancel block triggered with PERMISSION_DENIED
})
Ява
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference ref = database.getReference("records");
ref.addListenerForSingleValueEvent(new ValueEventListener() {
  @Override
  public void onDataChange(DataSnapshot snapshot) {
    // success method is not called
  }

  @Override
  public void onCancelled(FirebaseError firebaseError) {
    // error callback triggered with PERMISSION_DENIED
  });
});
ОТДЫХАТЬ
curl https://docs-examples.firebaseio.com/rest/records/
# response returns a PERMISSION_DENIED error

Поскольку операция чтения в /records/ является атомарной, и нет правила чтения, предоставляющего доступ ко всем данным в /records/ , это вызовет ошибку PERMISSION_DENIED . Если мы оценим это правило в симуляторе безопасности в нашей консоли Firebase , то увидим, что операция чтения была отклонена:

Attempt to read /records with auth=Success(null)
    /
    /records

No .read rule allowed the operation.
Read was denied.

Операция была отклонена, так как ни одно правило чтения не разрешало доступ к пути /records/ , но обратите внимание, что правило для rec1 никогда не оценивалось, поскольку его не было в запрошенном нами пути. Чтобы получить rec1 , нам нужно получить к нему прямой доступ:

JavaScript
var db = firebase.database();
db.ref("records/rec1").once("value", function(snap) {
  // SUCCESS!
}, function(err) {
  // error callback is not called
});
Цель-C
Примечание. Этот продукт Firebase недоступен для цели App Clip.
FIRDatabaseReference *ref = [[FIRDatabase database] reference];
[[ref child:@"records/rec1"] observeSingleEventOfType:FEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) {
    // SUCCESS!
}];
Быстрый
Примечание. Этот продукт Firebase недоступен для цели App Clip.
var ref = FIRDatabase.database().reference()
ref.child("records/rec1").observeSingleEventOfType(.Value, withBlock: { snapshot in
    // SUCCESS!
})
Ява
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference ref = database.getReference("records/rec1");
ref.addListenerForSingleValueEvent(new ValueEventListener() {
  @Override
  public void onDataChange(DataSnapshot snapshot) {
    // SUCCESS!
  }

  @Override
  public void onCancelled(FirebaseError firebaseError) {
    // error callback is not called
  }
});
ОТДЫХАТЬ
curl https://docs-examples.firebaseio.com/rest/records/rec1
# SUCCESS!

Переменная местоположения

Правила базы данных реального времени поддерживают переменную $location для сопоставления сегментов пути. Используйте префикс $ перед вашим сегментом пути, чтобы сопоставить ваше правило с любыми дочерними узлами на пути.

  {
    "rules": {
      "rooms": {
        // This rule applies to any child of /rooms/, the key for each room id
        // is stored inside $room_id variable for reference
        "$room_id": {
          "topic": {
            // The room's topic can be changed if the room id has "public" in it
            ".write": "$room_id.contains('public')"
          }
        }
      }
    }
  }

Вы также можете использовать $variable параллельно с постоянными путями.

  {
    "rules": {
      "widget": {
        // a widget can have a title or color attribute
        "title": { ".validate": true },
        "color": { ".validate": true },

        // but no other child paths are allowed
        // in this case, $other means any key excluding "title" and "color"
        "$other": { ".validate": false }
      }
    }
  }