云存储参考的 Firebase 安全规则

Firebase Cloud Storage 安全规则用于确定谁对 Cloud Storage 中存储的文件具有读写访问权限,以及文件的结构方式及其包含的元数据。云存储安全规则由考虑requestresource以允许或拒绝所需操作(例如上传文件或检索文件元数据)的规则组成。这些参考文档涵盖规则类型、 requestresource的属性、云存储安全规则使用的数据类型以及错误的发生方式。

规则

rule是一个表达式,通过计算来确定是否允许request执行所需的操作。

类型

允许

allow规则由方法(例如readwrite以及可选条件组成。执行规则时,会评估条件,如果条件评估为true ,则允许所需的方法;否则,该方法将被拒绝。无条件的allow规则始终允许所需的方法。

// Always allow method
allow <method>;

// Allow method if condition is true
allow <method>: if <condition>;

目前, allow是唯一受支持的规则类型。

请求方法

read方法涵盖了所有读取文件数据或元数据的请求,包括文件下载和文件元数据读取。

// Always allow reads
allow read;

// Allow reads if condition evaluates to true
allow read: if <condition>;

write方法涵盖了所有写入文件数据或元数据的请求,包括文件上传、文件删除、文件元数据更新。

// Always allow writes
allow write;

// Allow writes if condition evaluates to true
allow write: if <condition>;

匹配

当用户request (例如文件上传或下载)与规则所覆盖的文件路径匹配时,就会执行规则。 match由路径和正文组成,其中必须至少包含一条allow规则。如果没有匹配的路径,则拒绝该请求。

您可以match完整命名的路径,也可以插入通配符来匹配符合特定模式的所有路径。

路径段

single_segment

您可以使用单个路径段创建与 Cloud Storage 中存储的文件匹配的规则。

// Allow read at "path" if condition evaluates to true
match /path {
  allow read: if <condition>;
}

还允许多个路径段和嵌套路径:

// Allow read at "path/to/object" if condition evaluates to true
match /path {
  match /to {
    match /object {
      allow read: if <condition>;
    }
  }
}

{single_segment_wildcard}

如果要将规则应用于同一路径下的多个文件,可以使用通配符路径段来匹配某个路径下的所有文件。通过将变量括在大括号中来在路径中声明通配符变量: {variable} 。该变量可以在 match 语句中作为string访问。

// Allow read at any path "/*", if condition evaluates to true
match /{single_path} {
  // Matches "path", "to", or "object" but not "path/to/object"
  allow read: if <condition>;
}

多个路径段和嵌套路径也可能有通配符:

// Allow read at any path "/path/*/newPath/*", if condition evaluates to true
match /path/{first_wildcard} {
  match /newPath/{second_wildcard} {
    // Matches "path/to/newPath/newObject" or "path/from/newPath/oldObject"
    allow read: if <condition>;
  }
}

{multi_segment_wildcard=**}

如果要匹配路径上或路径下的任意数量的路径段,可以使用多段通配符,它​​将匹配该位置及其下的所有请求。这对于为用户提供自己的自由形式存储空间,或创建匹配许多不同路径段的规则(例如创建公共可读的文件集,或要求对所有写入进行身份验证)非常有用。

多段通配符路径的声明方式与单段通配符类似,只是在变量末尾添加=**{variable=**} 。多段通配符变量在 match 语句中可用作path对象。

// Allow read at any path "/**", if condition evaluates to true
match /{multi_path=**} {
  // Matches anything at or below this, from "path", "path/to", "path/to/object", ...
  allow read: if <condition>;
}

要求

request变量在条件中提供,以表示在该路径上发出的请求。 request变量有许多属性,可用于决定是否允许传入请求。

特性

auth

当经过身份验证的用户对 Cloud Storage 执行请求时, auth变量将填充用户的uid ( request.auth.uid ) 以及 Firebase 身份验证 JWT ( request.auth.token ) 的声明。

request.auth.token包含以下部分或全部键:

场地描述
email与帐户关联的电子邮件地址(如果存在)。
email_verified如果用户已验证他们有权访问email地址,则为true 。一些提供商会自动验证他们拥有的电子邮件地址。
phone_number与帐户关联的电话号码(如果存在)。
name用户的显示名称(如果已设置)。
sub用户的 Firebase UID。这在项目中是独一无二的。
firebase.identities与该用户帐户关联的所有身份的字典。字典的键可以是以下任意一个: emailphonegoogle.comfacebook.comgithub.comtwitter.com 。字典的值是与帐户关联的每个身份提供者的唯一标识符的数组。例如, auth.token.firebase.identities["google.com"][0]包含与该帐户关联的第一个 Google 用户 ID。
firebase.sign_in_provider用于获取此令牌的登录提供程序。可以是以下字符串之一: custompasswordphoneanonymousgoogle.comfacebook.comgithub.comtwitter.com
firebase.tenant与帐户关联的tenantId(如果存在)。例如tenant2-m6tyz

如果使用自定义身份验证, request.auth.token还包含开发人员指定的任何自定义声明。

当未经身份验证的用户执行请求时, request.authnull

// Allow requests from authenticated users
allow read, write: if request.auth != null;

path

path变量包含正在执行request的路径。

// Allow a request if the first path segment equals "images"
allow read, write: if request.path[0] == 'images';

resource

resource变量包含正在上传的文件的元数据或现有文件的更新元数据。这与resource变量有关,该变量包含请求路径上的当前文件元数据,而不是新元数据。

// Allow a request if the new value is smaller than 5MB
allow read, write: if request.resource.size < 5 * 1024 * 1024;

request.resource包含resource中的以下属性:

财产
name
bucket
metadata
size
contentType

time

time变量包含一个时间戳,表示正在评估请求的当前服务器时间。您可以使用它来提供对文件的基于时间的访问,例如:仅允许在特定日期之前上传文件,或者仅允许在上传后一小时内读取文件。

// Allow a read if the file was created less than one hour ago
allow read: if request.time < resource.timeCreated + duration.value(1, 'h');

提供了许多函数来使用时间戳持续时间编写规则。

资源

resource变量包含 Cloud Storage 中文件的文件元数据,例如文件名、大小、创建时间和自定义元数据。

特性

name

包含文件全名的字符串,包括文件的路径。

// Allow reads if the resource name is "path/to/object"
allow read: if resource.name == 'path/to/object'

bucket

包含存储此文件的Google Cloud Storage存储桶的字符串。

// Allow reads of all resources in your bucket
allow read: if resource.bucket == '<your-cloud-storage-bucket>'

generation

包含文件的 Google Cloud Storage对象生成的int。用于对象版本控制。

// Allow reads if the resource matches a known object version
allow read: if resource.generation == <known-generation>

metageneration

包含文件的 Google Cloud Storage对象元生成的int。用于对象版本控制。

// Allow reads if the resource matches a known object metadata version
allow read: if resource.metageneration == <known-generation>

size

包含文件大小(以字节为单位)的 int。

// Allow reads if the resource is less than 10 MB
allow read: if resource.size < 10 * 1024 * 1024;

timeCreated

表示文件创建时间的时间戳。

// Allow reads if the resource was created less than an hour ago
allow read: if resource.timeCreated < request.time + duration.value(60, "m")

updated

表示文件上次更新时间的时间戳。

// Allow reads if the resource was updated less than an hour ago
allow read: if resource.updated < request.time + duration.value(60, "m")

md5Hash

包含文件MD5 哈希值的字符串。

// Allow writes if the hash of the uploaded file is the same as the existing file
allow write: if request.resource.md5Hash == resource.md5Hash;

crc32c

包含文件的crc32c 哈希值的字符串。

// Allow writes if the hash of the uploaded file is the same as the existing file
allow write: if request.resource.crc32c == resource.crc32c;

etag

包含文件etag的字符串。

// Allow writes if the etag matches a known object etag
allow write: if resource.etag == <known-generation>

contentDisposition

包含文件内容配置的字符串。

// Allow reads if the content disposition matches a certain value
allow read: if resource.contentDisposition == 'inlined';

contentEncoding

包含文件内容编码的字符串。

// Allow reads if the content is encoded with gzip
allow read: if resource.contentEncoding == 'gzip';

contentLanguage

包含文件内容语言的字符串。

// Allow reads if the content language is Japanese
allow read: if resource.contentLanguage == 'ja';

contentType

包含文件内容类型的字符串。

// Allow reads if the content type is PNG.
allow read: if resource.contentType == 'image/png';

metadata

包含开发人员提供的其他元数据字段的Map<String, String>

// Allow reads if a certain metadata field matches a desired value
allow read: if resource.metadata.customProperty == 'customValue';

firestore.get 和 firestore.exists

firestore.get()firestore.exists()函数允许您访问 Cloud Firestore 中的文档以评估复杂的授权标准。

firestore.get()firestore.exists()函数都需要完全指定的文档路径。当使用变量构造firestore.get()firestore.exists()的路径时,需要使用$(variable)语法显式转义变量。

firestore.get

获取 Cloud Firestore 文档的内容。

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.uid)).data.memberships
    }
  }
}

firestore.存在

检查 Cloud Firestore 文档是否存在。

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.uid))
    }
  }
}

服务

service是云存储安全规则文件中的第一个声明,并指定这些规则将应用于哪个服务。

姓名

name

将应用服务规则的名称。当前唯一的值是firebase.storage

// Specify the service name
service firebase.storage {
  match /b/{bucket}/o {
    ...
  }
}

数据类型

规则语言允许您使用is运算符检查类型。

// For example
a is null
a is string

null

null数据类型表示不存在的值。

allow read: if request.auth != null;

bool

bool类型表示布尔truefalse值。

allow read: if true;   // always succeeds
allow write: if false; // always fails

比较

可以使用==运算符!=来比较布尔值。

布尔运算

手术表达
AND x && y
OR x || y
NOT !x

操作短路,并且可以返回truefalseError

allow read: if true || false;   // always succeeds, short circuits at true
allow write: if false && true; // always fails, short circuits at false

intfloat

intfloat类型表示数字。整数为: 01-2等,而浮点数为: 1.0-2.03.33等。

整数是带符号的 64 位值,浮点数是符合 IEEE 754 标准的 64 位值。当用于float值的比较和算术运算时, int类型的值将被强制转换为float型。

比较

可以使用==!=><>=<=运算符对整数和浮点数进行比较和排序。

算术

整数和浮点数可以进行加、减、乘、除、取模和取反:

手术表达
添加x + y
减法x - y
乘法x * y
分配x / y
模数x % y
否定-x

数学函数

Firebase Cloud Storage 安全规则还提供了许多数学辅助函数来简化表达式:

功能描述
math.ceil(x)数值上限
math.floor(x)数值下限
math.round(x)将输入值舍入为最接近的 int
math.abs(x)输入的绝对值
math.isInfinite(x)测试值是否为±∞ ,返回bool
math.isNaN(x)测试该值是否不是数字NaN ,返回bool

string

比较

可以使用==!=><>=<=运算符对字符串进行字典顺序比较和排序。

级联

可以使用+运算符连接字符串。

// Concatenate a file name and extension
'file' + '.txt'

指数及范围

index运算符string[]返回一个字符串,其中包含字符串中提供的索引处的字符。

// Allow reads of files that begin with 'a'
match /{fileName} {
  allow read: if fileName[0] == 'a';
}

range运算符string[i:j]返回一个字符串,其中包含指定索引之间的字符,从i (包含)到j (不包含)。如果未指定ij ,则它们分别默认为 0 和字符串的大小,但至少必须指定ij才能使范围有效。

// Allow reads of files that begin with 'abcdef'
match /{fileName} {
  allow read: if fileName[0:6] == 'abcdef';
}

如果提供的索引超出字符串范围, indexrange运算符将产生错误。

size

返回字符串中的字符数。

// Allow files with names less than 10 characters
match /{fileName} {
  allow write: if fileName.size() < 10;
}

matches

执行正则表达式匹配,如果字符串与给定的正则表达式匹配,则返回true 。使用Google RE2 语法

// Allow writes to files which end in ".txt"
match /{fileName} {
  allow write: if fileName.matches('.*\\.txt')
}

split

根据提供的正则表达式拆分字符串并返回字符串list 。使用Google RE2 语法

// Allow files named "file.*" to be uploaded
match /{fileName} {
  allow write: if fileName.split('.*\\..*')[0] == 'file'
}

path

路径是类似目录的名称,具有可选的模式匹配。正斜杠/的存在表示路径段的开始。

path

string参数转换为path

// Allow reads on a specific file path
match /{allFiles=**} {
  allow read: if allFiles == path('/path/to/file');
}

timestamp

时间戳采用 UTC 格式,可能的值从 0001-01-01T00.00.00Z 开始,到 9999-12-31T23.59.59Z 结束。

比较

可以使用==!=><>=<=运算符对时间戳进行比较和排序。

算术

时间戳支持时间戳和时长之间的加法和减法,如下:

表达结果
timestamp + duration timestamp
duration + timestamp timestamp
timestamp - duration timestamp
timestamp - timestamp duration
duration + duration duration
duration - duration duration

date

仅包含yearmonthdaytimestamp值。

// Allow reads on the same day that the resource was created.
allow read: if request.time.date() == resource.timeCreated.date()

year

年份值是一个整数,从 1 到 9999。

// Allow reads on all requests made before 2017
allow read: if request.time.year() < 2017

month

月份值,为整数,从 1 到 12。

// Allow reads on all requests made during the month of January
allow read: if request.time.month() == 1;

day

本月的当前日期,为整数,从 1 到 31。

// Allow reads on all requests made during the first day of each month
allow read: if request.time.day() == 1;

time

包含当前时间的duration值。

// Allow reads on all requests made before 12PM
allow read: if request.time.time() < duration.time(12, 0, 0, 0);

hours

小时值是一个整数,从 0 到 23。

// Allow reads on all requests made before 12PM
allow read: if request.time.hours() < 12;

minutes

分钟值是一个整数,从 0 到 59。

// Allow reads during even minutes of every hour
allow read: if request.time.minutes() % 2 == 0;

seconds

秒值作为 int,从 0 到 59。

// Allow reads during the second half of each minute
allow read: if request.time.seconds() > 29;

nanos

以纳秒为单位的整数秒小数部分。

// Allow reads during the first 0.1 seconds of each second
allow read: if request.time.nanos() < 100000000;

dayOfWeek

一周中的某一天,从 1(星期一)到 7(星期日)。

// Allow reads on weekdays (Monday to Friday)
allow read: if request.time.dayOfWeek() < 6;

dayOfYear

当前年份中的第几天,从 1 到 366。

// Allow reads every fourth day
allow read: if request.time.dayOfYear() % 4 == 0;

toMillis

返回自 Unix 纪元以来的当前毫秒数。

// Allow reads if the request is made before a specified time
allow read: if request.time.toMillis() < <milliseconds>;

duration

持续时间值表示为秒加上以纳秒为单位的小数秒。

比较

可以使用==!=><>=<=运算符对持续时间进行比较和排序。

算术

持续时间支持时间戳和持续时间之间的加法和减法,如下所示:

表达结果
timestamp + duration timestamp
duration + timestamp timestamp
timestamp - duration timestamp
timestamp - timestamp duration
duration + duration duration
duration - duration duration

seconds

当前持续时间的秒数。必须介于 -315,576,000,000 和 +315,576,000,000 之间(含)。

nanos

当前持续时间的小数秒数(以纳秒为单位)。必须介于 -999,999,999 和 +999,999,999 之间(含)。对于非零秒和非零纳秒,两者的符号必须一致。

duration.value

可以使用duration.value(int magnitude, string units)函数创建持续时间,该函数根据给定的幅度和单位创建持续时间。

// All of these durations represent one hour:
duration.value(1, "h")
duration.value(60, "m")
duration.value(3600, "s")

可能的unit有:

期间unit
周数w
d
小时h
分钟m
秒数s
毫秒ms
纳秒ns

duration.time

可以使用duration.time(int hours, int minutes, int seconds, int nanoseconds)函数创建持续时间,该函数创建给定小时、分钟、秒和纳秒的持续时间。

// Create a four hour, three minute, two second, one nanosecond duration
duration.time(4, 3, 2, 1)

list

列表包含值的有序数组,其类型可以是: nullboolintfloatstringpathlistmaptimestampduration

给定list类型的xy以及int类型的ij

创建

要创建列表,请在括号之间添加值:

// Create a list of strings
['apples', 'grapes', 'bananas', 'cheese', 'goats']

比较

可以使用==运算符!=来比较列表。两个列表的相等要求所有值都相等。

指数及范围

index运算符list[]返回列表中提供的索引处的项目。

// Allow reads of all files that begin with 'a'
match /{fileName} {
  allow read: if fileName[0] == 'a';
}

range运算符list[i:j]返回列表中指定索引之间的所有项目,从i (包含)到j (不包含)。如果未指定ij ,则它们分别默认为 0 和列表的大小,但至少必须指定ij才能使范围有效。

// Allow reads of all files that begin with 'abcdef'
match /{fileName} {
  allow read: if fileName[0:6] == 'abcdef';
}

in

如果列表中存在所需值,则返回true ;如果不存在,则返回false

// Allow read if a filename has the string 'txt' in it
match /{fileName} {
  allow read: if 'txt' in fileName.split('\\.');
}

join

将字符串列表组合成单个字符串,并用给定字符串分隔。

// Allow reads if the joined array is 'file.txt'
allow read: if ['file', 'txt'].join('.') == 'file.txt';

size

列表中的项目数。

// Allow read if there are three items in our list
allow read: if ['foo', 'bar', 'baz'].size() == 3;

hasAll

如果列表中存在所有值,则返回true

// Allow read if one list has all items in the other list
allow read: if ['file', 'txt'].hasAll(['file', 'txt']);

map

映射包含键/值对,其中键是字符串,值可以是以下任意值: nullboolintfloatstringpathlistmaptimestampduration

创建

要创建映射,请在大括号之间添加键/值对:

// Create a map of strings to strings
{
  'mercury': 'mars',
  'rain': 'cloud',
  'cats': 'dogs',
}

比较

可以使用==运算符!=来比较地图。两个映射相等要求两个映射中都存在所有键并且所有值都相等。

指数

映射中的值可以通过使用括号或点表示法来访问:

// Access custom metadata properties
allow read: if resource.metadata.property == 'property'
allow write: if resource.metadata['otherProperty'] == 'otherProperty'

如果密钥不存在,将返回error

in

如果映射中存在所需的键,则返回true ;如果不存在,则返回false

// Allow reads if a property is present in the custom metadata
allow read: if property in resource.metadata;

size

地图中键的数量。

// Allow reads if there's exactly one custom metadata key
allow read: if resource.metadata.size() == 1;

keys

地图中所有键的列表。

// Allow reads if the first metadata key is 'myKey'
allow read: if resource.metadata.keys()[0] == 'myKey';

values

映射中所有值的列表(按键顺序排列)。

// Allow reads if the first metadata value is 'myValue'
allow read: if resource.metadata.values()[0] == 'myValue';

错误

误差评估

遇到错误时,Firebase Cloud Storage 安全规则会继续评估。这很有用,因为条件&&||如果条件分别短路为falsetrue ,则表达式可能会吸收错误。例如:

表达结果
error && true error
error && false false
error || true true
error || false error

引发错误的常见位置有:除以零、访问列表或映射中不存在的值以及将错误类型的值传递给函数。

// Error if resource.size is zero
allow read: if 1000000 / resource.size;

// Error, key doesn't exist
allow read: if resource.metadata.nonExistentKey == 'value';

// Error, no unit 'y' exists
allow read: if request.time < resource.timeCreated + duration.value(1, 'y');