Catch up on everthing we announced at this year's Firebase Summit. Learn more

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

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

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

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

Cloud 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 /cities/SF или использовать маски , чтобы указать на какой - либо документ по указанному пути, как и в match /cities/{city} .

В приведенном выше примере оператор матча использует {city} подстановочные синтаксис. Это означает , что правило применяется к любому документу в cities коллекции, такие как /cities/SF или /cities/NYC - /cities/NYC . Когда allow выражения в операторе матча оцениваются, то city переменный будет решать имя документа города, такие как SF или NYC - 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 заявления. В случае , когда несколько allow выражения сопоставляются запрос, доступ разрешен , если какое - либо из условий является true :

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 (не разрешено)
Максимальное количество выражений, оцениваемых за запрос 1,000
Максимальный размер набора правил Наборы правил должны соответствовать двум ограничениям по размеру:
  • предел 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 пути к файлам , используемые для файлов доступа в Cloud Storage. Правила могут 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 .

В приведенных выше правил, файл «изображения / profilePhoto.png» можно прочитать , если либо condition или other_condition оценить истинно, в то время как файл «изображения / пользователей / пользователь: 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 отправляется в Cloud Storage. request переменный содержит путь к файлу , где запрос, выполняемый, время , когда запрос получен, и новый resource значение , если запрос на записи. Заголовки HTTP и состояние аутентификации также включены.

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

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

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

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

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

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

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

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

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

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

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

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

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.
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.
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.
FIRDatabaseReference *ref = [[FIRDatabase database] reference];
[[ref child:@"records/rec1"] observeSingleEventOfType:FEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) {
    // SUCCESS!
}];
Быстрый
Примечание: Этот Firebase продукт не доступен на цели клипа App.
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!

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

Realtime Правило баз данных поддерживает $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 }
      }
    }
  }