向公众公开 Firebase apiKey 是否安全?

Is it safe to expose Firebase apiKey to the public?

提问人:farmio 提问时间:5/27/2016 最后编辑:hc_devfarmio 更新时间:9/6/2023 访问量:258378

问:

Firebase Web-App 指南指出,我应该将给定的内容放在我的 Html 中以初始化 Firebase:apiKey

// TODO: Replace with your project's customized code snippet
<script src="https://www.gstatic.com/firebasejs/3.0.2/firebase.js"></script>
<script>
  // Initialize Firebase
  var config = {
    apiKey: '<your-api-key>',
    authDomain: '<your-auth-domain>',
    databaseURL: '<your-database-url>',
    storageBucket: '<your-storage-bucket>'
  };
  firebase.initializeApp(config);
</script>

通过这样做,每个访问者都会看到。apiKey

该密钥的目的是什么,它真的是公开的吗?

JavaScript 的Firebase

评论

1赞 Oliver Schafeld 4/27/2019
用户 Christophe Quintard 添加了一个指向非常有用的文章的链接,其中包含有关 Firebase API 安全性的其他信息,因此我在此处重新发布它: javebratt.com/hide-firebase-api (该评论将消失,因为它附加到另一个用户的答案上,该答案因质量差而被标记为删除)
15赞 ICW 12/22/2019
我只想指出,仅仅因为这个特定的框架恰好可以公开它的 API,这并不意味着其他框架可以接受它。不希望任何人从这篇文章中走出来,认为“公开 API 密钥是可以的”。
4赞 B L Λ C K 1/21/2020
您可以毫无问题地公开密钥。为了安全起见,您可以在生产环境中将其限制为特定域,以便其他人无法从任何随机域名进行调用 API 调用。为了更安全,请从生产应用中删除 localhost。
6赞 forresthopkinsa 4/6/2020
我不认为从您的推荐人白名单中删除 localhost 除了使测试更难之外会做任何事情。该配置与 IP 白名单不同;可以把它想象成一个 CORS 配置。Firebase 的工作方式是,这些 API 路由是直接从客户端调用的,而不是代理的。这就是为什么您的网页需要 API 密钥的原因。如果不良行为者想要从 Postman 调用您的 API 路由,您的反向链接白名单不会阻止他们。它仅对防止其他公共站点从您的服务器中哄骗有用。
2赞 forresthopkinsa 4/6/2020
如果你想阻止对手通过 CURL 滥用你的 API,那么你需要实施其他对策,如身份验证和速率限制。这是一个面向 Internet 的 API。这是一件好事!!不是错误,而是功能。

答:

929赞 Frank van Puffelen 5/27/2016 #1

此配置代码段中的代码段仅标识您在 Google 服务器上的 Firebase 项目。有人知道它不是安全风险。事实上,他们必须知道这一点,以便他们与您的 Firebase 项目进行交互。使用 Firebase 作为后端的每个 iOS 和 Android 应用中也包含相同的配置数据。apiKey

从这个意义上说,它与在同一代码段中标识与您的项目关联的后端数据库的数据库 URL 非常相似:.请参阅以下问题,了解为什么这不是安全风险:如何限制 Firebase 数据修改?,包括使用 Firebase 的服务器端安全规则来确保只有授权用户才能访问后端服务。https://<app-id>.firebaseio.com

如果您想了解如何保护对 Firebase 后端服务的所有数据访问均已获得授权,请阅读有关 Firebase 安全规则的文档。这些规则控制对文件存储和数据库访问的访问,并在 Firebase 服务器上强制执行。因此,无论是您的代码,还是其他人的代码使用您的配置数据,它都只能执行安全规则允许它执行的操作。

有关 Firebase 将这些值用于哪些目的以及您可以为哪些值设置配额的其他说明,请参阅有关使用和管理 API 密钥的 Firebase 文档。


如果您想降低将此配置数据提交到版本控制的风险,请考虑使用 Firebase Hosting 的 SDK 自动配置功能。虽然密钥最终仍将以相同的格式出现在浏览器中,但它们将不再被硬编码到您的代码中。


更新(2021 年 5 月):借助名为 Firebase App Check 的新功能,现在可以将对 Firebase 项目中后端服务的访问限制为仅来自该特定项目中注册的 iOS、Android 和 Web 应用的后端服务。

您通常希望将其与上述基于用户身份验证的安全性相结合,以便您有另一道屏障,防止使用您的应用的滥用用户。

通过将 App Check 与安全规则相结合,您既可以广泛防止滥用,又可以很好地控制每个用户可以访问的数据,同时仍然允许从客户端应用程序代码直接访问数据库。

评论

16赞 Emmanuel Campos 6/12/2016
所以这意味着其他人将能够访问我的 firebase 数据库中的所有数据?
67赞 KhoPhi 6/12/2016
@EmmanuelCampos答案是肯定的和否定的。是的,如果您允许或希望其他人访问数据库中的所有数据。不,如果你不想让他们这样做。Firebase 数据库有规则,这些规则由您控制
13赞 Emmanuel Campos 6/13/2016
在这里找到了我最后一个问题的答案 support.google.com/firebase/answer/6400741 感谢您的帮助。此链接将来可能会对某人有所帮助。
17赞 Artem Arkhipov 2/13/2017
@m.rufca ,您的数据应该可供经过身份验证的用户使用。这就是诀窍。默认情况下,在您的 firebase 设置中,只有 localhost 和您的项目域有权从它们执行身份验证。因此,没有其他人可以创建通常与您的 firebase 一起使用的应用程序。
28赞 Muhammad Umer 3/15/2017
如果机器人在我的应用上创建无限用户怎么办?我怎样才能要求验证码。
130赞 now 12/5/2016 #2

根据 prufrofro 和 Frank van Puffelen 回答,我把这个设置放在一起,它不会阻止抓取,但会使使用 API 密钥变得更加困难。

警告:要获取您的数据,即使使用这种方法,也可以简单地在 Chrome 中打开 JS 控制台并输入:

firebase.database().ref("/get/all/the/data").once("value", function (data) {
    console.log(data.val());
});

只有数据库安全规则才能保护您的数据。

尽管如此,我还是将我的生产 API 密钥的使用限制在我的域名上,如下所示:

  1. https://console.developers.google.com/apis
  2. 选择您的 Firebase 项目
  3. 凭据
  4. 在“API 密钥”下,选择您的浏览器密钥。它应如下所示:“浏览器密钥(由 Google 服务自动创建)"
  5. 在“接受来自这些的请求 HTTP referrers (web sites)“,添加应用的 URL(例如:projectname.firebaseapp.com/* )

现在,该应用程序将仅适用于此特定域名。因此,我创建了另一个 API 密钥,该密钥对于本地主机开发来说是私有的。

  1. 单击 Create credentials > API Key

默认情况下,正如 Emmanuel Campos 所提到的,Firebase 仅将 localhost 和您的 Firebase 托管域列入白名单


为了确保我不会错误地发布错误的 API 密钥,我使用以下方法之一在生产中自动使用更受限制的密钥。

Create-React-App 的设置

在:/env.development

REACT_APP_API_KEY=###dev-key###

在:/env.production

REACT_APP_API_KEY=###public-key###

/src/index.js

const firebaseConfig = {
  apiKey: process.env.REACT_APP_API_KEY,
  // ... 
};

评论

5赞 steliosf 6/28/2017
这对你有用吗?正在考虑为 Android 应用程序做同样的事情。我想知道为什么 Firebase 没有在安全部分涵盖这一点。
3赞 now 6/29/2017
到目前为止,我没有遇到任何问题,但可能也没有攻击
5赞 thoutbeckers 7/4/2017
他们的指南中没有提到这一点,因为它不会保护您免受抓取。所有这些都确保了其他人无法制作使用您的 firebase 读取(或写入)数据的 Web 应用程序,如果它在正常运行的浏览器中运行。
1赞 Akash Arora 3/8/2019
在“接受来自这些 HTTP 引荐来源(网站)的请求”中,添加应用的 URL(例如:projectname.firebaseapp.com/* ) 这不起作用。
2赞 now 4/13/2020
@FrankvanPuffelen 据我了解,这并没有太大的区别,但滥用配额会稍微烦人一些,因为在表现良好的浏览器中,HTML/JS 提供的 API 密钥仅适用于预期的域,而不适用于 localhost 或其他任何东西。但我同意,与 Firebase 已经提供的保护相比,增加的保护是微不足道的。我会把答案改写成不那么戏剧化的东西。
39赞 Teoman shipahi 10/23/2017 #3

我不相信向客户端公开安全/配置密钥。我不会说它是安全的,不是因为有人可以从第一天起窃取所有私人信息,因为有人可以提出过多的要求,耗尽你的配额,让你欠谷歌很多钱。

您需要考虑许多概念,包括限制人们不要访问他们不应该访问的地方、DOS 攻击等。

我更希望客户端首先会攻击您的 Web 服务器,在那里您将第一手防火墙、验证码、cloudflare、自定义安全性放在客户端和服务器之间或服务器和 firebase 之间,然后您就可以开始了。至少您可以在可疑活动到达 firebase 之前首先阻止它。您将拥有更大的灵活性。

我只看到一个很好的使用场景,即使用基于客户端的配置进行内部使用。例如,你有内部域,你很确定外部人员无法访问那里,所以你可以设置浏览器等环境 -> firebase 类型。

评论

16赞 mbochynski 6/5/2018
但是,这难道不等同于“公开”任何其他 REST API?我的意思是使用 REST API URL 可供用户使用。他们可以使用该 URL 发出他们想要的任何请求并耗尽您的配额。firebase 所做的是使用带有 api 密钥的配置来识别您的后端部分,并且它必须可供用户发出请求。
7赞 Teoman shipahi 6/5/2018
@mbochynski,但您可以在某种程度上直接向导致您支付账单的资源发出请求。在Firebase方面,没有那么多的控制机制来防止DDoS攻击等。我的建议是让你的客户端调用你的 REST API,但 REST API 应该私下保存 API 密钥,甚至在你点击 Firebase 资源之前,验证它们是否是合法请求。(通过 Cloudflare 等)。或从缓存中检索结果。然后,只有在需要时,您才会访问Firebase资源。这就是我要实现的 firebase.google.com/docs/admin/setup
8赞 Nick Chan Abdullah 4/10/2019
在浏览器上暴露密钥是一个非常糟糕的主意。那些写所有这些指南/文章的人,他们在想什么?HTTP 反向链接的安全性?这很容易被欺骗
7赞 forresthopkinsa 4/6/2020
你们没有考虑这个。不要将 API 密钥视为机密;它不是私钥,它只是一个 ID,因此 Firebase API 知道谁在访问哪个项目。如果您想要很大的灵活性,并且需要控制服务器/客户端交互的每一步,那么您不应该使用 Firebase,而应该使用 GCP。
0赞 Teoman shipahi 4/20/2020
@forresthopkinsa我有上面的链接,请评论采取什么方法。这里没有人天真到认为它是一把秘密钥匙。
11赞 bzk 8/6/2019 #4

启用用户/密码注册时,API 密钥暴露会造成漏洞。有一个开放的 API 端点,它获取 API 密钥并允许任何人创建新的用户帐户。然后,他们可以使用这个新帐号登录受 Firebase 身份验证保护的应用,或使用 SDK 通过用户/传递进行身份验证并运行查询。

我已经向谷歌报告了这个问题,但他们说它正在按预期工作。

如果无法禁用用户/密码帐户,则应执行以下操作: 创建一个云函数以自动禁用新用户 onCreate,并创建一个新的数据库条目来管理他们的访问权限。

例如:MyUsers/{userId}/Access:0

exports.addUser = functions.auth.user().onCreate(onAddUser);
exports.deleteUser = functions.auth.user().onDelete(onDeleteUser);

更新规则,仅允许具有 1 访问权限的用户读取>。

如果侦听器函数没有足够快地禁用帐户,则读取规则将阻止它们读取任何数据。

评论

0赞 VaibS 7/14/2020
你说的是哪个 API?
0赞 bzk 7/15/2020
@VaibS Firebase 身份验证 REST API firebase.google.com/docs/reference/rest/auth
0赞 VaibS 7/16/2020
如果我们只将我们的域名列入白名单,这仍然是一个问题吗?
10赞 Bhupiister singh 4/13/2020 #5

我相信,一旦数据库规则写得准确,就足以保护你的数据。此外,可以遵循一些准则来相应地构建您的数据库。例如,在用户下创建一个 UID 节点,并将所有信息放在它下面。之后,您将需要实现一个简单的数据库规则,如下所示

  "rules": {
    "users": {
      "$uid": {
        ".read": "auth != null && auth.uid == $uid",
        ".write": "auth != null && auth.uid == $uid"
      }
    }
  }
}

其他用户将无法读取其他用户的数据,此外,域策略将限制来自其他域的请求。 可以在Firebase安全规则上阅读更多关于它的信息

8赞 Berci 4/30/2020 #6

虽然回答了最初的问题(API 密钥可以公开 - 必须从数据库 rulles 设置数据保护),但我也在寻找一种解决方案来限制对数据库特定部分的访问。 因此,在阅读了这篇文章以及一些关于可能性的个人研究之后,我想出了一种稍微不同的方法来限制未经授权的用户的数据使用:

我也将我的用户保存在我的数据库中,在相同的 uid 下(并将配置文件数据保存在那里)。所以我只是像这样设置数据库规则:

".read": "auth != null && root.child('/userdata/'+auth.uid+'/userRole').exists()",
".write": "auth != null && root.child('/userdata/'+auth.uid+'/userRole').exists()"

这样,只有以前保存的用户才能在数据库中添加新用户,因此没有帐户的任何人都无法在数据库上执行操作。

此外,只有当用户具有特殊角色并且仅由管理员或该用户本身进行编辑时,才可以添加新用户(如下所示):

"userdata": {
  "$userId": {
    ".write": "$userId === auth.uid || root.child('/userdata/'+auth.uid+'/userRole').val() === 'superadmin'",
   ...
8赞 JustRaman 12/16/2020 #7

暴露 API 密钥不会带来安全风险,但任何人都可以将您的凭据放在他们的网站上。

开放 api 密钥会导致攻击,这些攻击可能会在 firebase 上占用大量资源,这肯定会花费您的辛勤资金。

您始终可以将 firebase 项目密钥限制为域/IP。

https://console.cloud.google.com/apis/credentials/key

选择您的项目 ID 和密钥,并将其限制为您的 Android/iOs/web 应用程序。

6赞 phoenixstudio 2/28/2021 #8

可以包含它们,并且仅在 Firebase ML 或使用 Firebase 身份验证时需要特别小心

Firebase 的 API 密钥与典型的 API 密钥不同:与通常使用 API 密钥的方式不同,Firebase 服务的 API 密钥不用于控制对后端资源的访问;这只能通过 Firebase 安全规则来完成。通常,您需要严格保护 API 密钥(例如,通过使用保管库服务或将密钥设置为环境变量);但是,Firebase 服务的 API 密钥可以包含在代码或签入的配置文件中。

尽管 Firebase 服务的 API 密钥可以安全地包含在代码中,但在一些特定情况下,您应该对 API 密钥实施限制;例如,如果您使用的是 Firebase ML 或将 Firebase Authentication 与电子邮件/密码登录方法结合使用。在本页后面部分了解有关这些案例的更多信息。

有关更多信息,请查看官方文档

0赞 Muhammad Hamid Rafique 9/6/2023 #9

仅身份验证工作流程就遇到了类似的问题。如果您从前端在 firebase 中创建用户帐户,我会请您重新考虑这一点。如果您没有任何后端代码作为创建 firebase 用户的预触发器运行,用于检查用户是否来自您的应用,则应切换到从后端创建用户,并在管理控制台中停用所有注册/登录方法。由于后端服务帐户密钥与前端不同,因此它们不会公开,您可以通过自己喜欢的凭据管理器(环境变量等)来管理它们。如果您的主数据库不是 Firestore(许多大量使用 SQL 但需要 firebase 来实现某些实时功能的企业应用就是这种情况),并且您不需要用户的显式凭据即可直接登录 firebase,这也非常有用。在后端,可以使用以下方法创建自定义令牌

getAuth()
  .createCustomToken(uid)
  .then((customToken) => {
    // Send token back to client
  })
  .catch((error) => {
    console.log('Error creating custom token:', error);
  }); 

然后,可以将此自定义令牌传递给客户端,客户端可以使用 webSDK 登录到第一个

firebase.auth().signInWithCustomToken(customToken)
  .then((userCredential) => {
    // Signed in
    var user = userCredential.user;
    // ...
  })
  .catch((error) => {
    var errorCode = error.code;
    var errorMessage = error.message;
    // ...
  });

您仍然需要非常强大和全面的安全规则,以便访问是细粒度的,并且适用于 RBAC。但是,对我来说,问题在于使用客户端的 webSDK 配置密钥,用户可以创建一个帐户或登录我的 firebase 项目。但是,通过这种方式,可以确保用户在不通过您自己的后端的情况下无法创建帐户并登录。