【问题标题】:How do I hide API key in create-react-app?如何在 create-react-app 中隐藏 API 密钥?
【发布时间】:2018-07-19 20:51:30
【问题描述】:

我在 create-react-app 中制作了一个天气应用程序。如何隐藏 API 密钥以便可以提交到 GitHub?

现在关键在 App.js 中: const API_KEY = "123456";

【问题讨论】:

标签: reactjs api github create-react-app api-key


【解决方案1】:

不幸的是,即使您使用 gitignore 和 .env 文件,在您的 React 客户端中保留任何密钥也是不安全的。正如@ClaudiuCreanga 所指出的,React 环境变量嵌入在构建中并且可以公开访问。

您实际上应该只在您的后端(例如 Node / Express)中保存 API 密钥或机密。您可以让您的客户端向您的后端 API 发送请求,然后后端 API 可以使用 API 密钥进行实际的 API 调用并将数据发送回您的客户端。

【讨论】:

  • 这是正确答案。在浏览器上运行的客户端应用程序无法安全地存储机密。
  • 这应该是公认的答案。我想知道有多少人正在部署不安全的应用程序,因为没有阅读本文并依赖其他人的答案。
  • @techi 理想情况下,在 React 前端应用程序中,用户是提供凭据(用户名/密码)以访问后端(具有身份验证服务的 Node/Express)的用户,后端会为其发送生成的用于前端存储在内存中的令牌(未嵌入 HTML/JS 等)。所以现在,前端可以告诉后端访问第三方 API 等。这样我们可以缓解前端暴露的攻击面,并在后端隐藏第三方 API 令牌。
  • @devuxer 是的,如果 API 密钥被设计为公开/在前端使用,例如您使用 Google Maps Javascript API 的示例,那么可以在那里使用它。
  • 这比其他答案要好,但即使在运行时请求它,他们仍然可以查看网络流量并查看 api 密钥是什么,对吗?我的意思是,你可以尝试以某种方式混淆它,但它仍然是同样的问题。据我所知,没有办法在前端应用程序上真正保密 api 密钥。我错了吗?
【解决方案2】:

免责声明

警告:请勿在您的 反应应用!

环境变量嵌入到构建中,这意味着任何人都可以 通过检查应用的文件来查看它们。

以下答案提供了在环境变量中存储非机密数据的正确方法。 请记住,秘密数据可以通过开发人员工具访问,因此存储为环境变量是不安全的。如果您想存储一些秘密数据,那么存储在后端是更好的选择,如果客户端想要访问秘密数据,它可以通过向服务器发出请求来访问。 (有关存储秘密数据的更多详细信息,请参阅@Antonia 的回答。)

事实证明,create-react-app 有一些内置功能可以帮助您。谢谢George Karametas 的洞察力。要访问该功能,您需要:

1。在项目目录的根目录中创建一个名为 .env 的文件。

- your_project_folder
  - node_modules
  - public
  - src
  - .env         <-- create it here
  - .gitignore
  - package-lock.json
  - package.json

2。在 .env 文件中,将 REACT_APP_ 添加到您选择的 API 密钥名称并分配它。

create-react-app 工具使用REACT_APP_ 来识别这些变量。如果您的 API 密钥名称不以它开头,create-react-app 将看不到它。

// .env

REACT_APP_API_KEY=your_api_key  <-- yes
API_KEY=your_api_key            <-- no

// Example (from 이준형's response):
REACT_APP_WEATHER_API_KEY=123456

3。将.env 文件添加到您的.gitignore 文件中。

添加以下行后,保存.gitignore 文件并执行git status 以确保您的.env 文件不会在git 中显示为新文件。

// .gitignore

# api keys
.env       <-- add this line

# dependencies
/node_modules
...

4。通过 process.env 对象访问 API 密钥。

要检查您是否可以访问您的 API 密钥,请转到您的 App.js 文件并在 require 语句下方的顶部添加 console.log。保存文件并重新加载页面后,如果控制台日志没有显示您的 API 密钥,请尝试重新启动反应服务器。请务必在提交代码之前删除控制台日志行。

// src/App.js

import React, { Component } from 'react';
import './App.css';

console.log(process.env.REACT_APP_WEATHER_API_KEY)

class App extends Component {
...

【讨论】:

  • 我得到一个未定义的。我们必须通过 App.js 文件导入还是必须导出 .env?
  • @born2gamble 确保将 apiKey 包装在模板文字中,因为它必须是字符串。这是我的设置示例:pastebin.com/WQ0CzqQy 然后确保重新启动服务器。
  • 客户端打开devtools不会暴露吗?
  • 这对于秘密来说绝对不安全。 .env 中的任何内容都可以在开发工具中公开检查。处理此类必须在客户端保密的值的唯一方法是通过将为您处理它的服务器代理请求。请参阅 ClaudiuCreanga 对已接受答案的评论。
  • @richardsonae 那么它在生产中是如何工作的呢?生产就绪代码如何知道在哪里访问密钥?
【解决方案3】:

警告

除非您正在制作教程应用程序,否则不要将 API 密钥等机密信息放在客户端源代码(例如 React 应用程序)中。 From Create React App's documentation:

警告:请勿在您的 反应应用!

环境变量嵌入到构建中,这意味着任何人都可以 通过检查应用的文件来查看它们。

首先,在项目的根目录中创建一个 .env 文件,即在 src 文件夹之外运行 react-scripts start(或 yarn start)的位置。

然后,添加

REACT_APP_WEATHER_API_KEY=123456

在提交之前,你应该排除这个 .env 文件,以便找到 .gitignore 文件并添加 .env。

变量的名称需要以REACT_APP_ 开头,这样可以防止您在构建时意外包含机密。

不要忘记在 .gitignore 文件中添加 .env。


在代码中使用环境变量:

const API_KEY = process.env.REACT_APP_WEATHER_API_KEY;

为了在将环境变量添加到.env 后读取它们,请重新启动您的服务器。

【讨论】:

  • 您应该重新启动应用程序以更新您的密钥。
  • 这个被接受的答案如何? facebook.github.io/create-react-app/docs/…: 警告:不要在你的 React 应用程序中存储任何秘密(例如私有 API 密钥)!环境变量嵌入到构建中,这意味着任何人都可以通过检查应用程序的文件来查看它们。
  • @ClaudiuCreanga 那么解决方案是什么?我们应该在 api 密钥和浏览器请求之间添加一个节点 js 服务器吗?这样一来,我们的节点服务器就成为唯一持有 api 密钥并使用存储在节点 js 服务器中的秘密 api 密钥代表用户发出第三方请求的节点。
  • @theprogrammer 是的,类似于处理请求的快速服务器。这是唯一的方法。
  • @Dickens 你当然可以这样做。该方法并非毫无价值,因为您可能有不同的 api 密钥用于开发和生产。 env 文件可以干净地分离那些 api 键...
【解决方案4】:

来自反应documentation

警告:不要在您的 React 应用中存储任何机密(例如私有 API 密钥)!

环境变量嵌入到构建中,这意味着任何人都可以 通过检查应用的文件来查看它们。

【讨论】:

  • 那我们该怎么办呢?将其存储在服务器端?在某些时候,前端需要知道 api 密钥......此时任何人都可以查看它。如果有人知道这方面的任何好文章,请分享。
  • @TrevorWood 是的,将它存储在服务器端并在那里进行实际的 API 调用。后端应该充当 React 应用程序的代理,存储秘密,进行 API 调用,然后发回数据。例如,在 React / Express 应用程序中,您可以创建一个 Express 端点来获取天气。您从 React 调用端点,然后 Express 使用 API 密钥在响应之前获取第 3 方天气数据,以便 React 可以显示数据。 rockyourcode.com/secret-keys-in-react
  • @AntoniaBlair 没有将它们放入应用程序的原因是人们无法读取构建文件来查看 API 密钥。但是人们不能在客户端加载网站后提取 API 密钥吗?一旦前端拥有 API 密钥或前端请求 API 密钥。
  • @TrevorWood 不要将 API 密钥传递给前端,将其保存在后端保密的地方。将其视为后端进行 API 调用的合作伙伴关系,但前端可以向后端请求数据。见 cmets:hashnode.com/post/…
  • 这个问题的答案如何?
【解决方案5】:

虽然@Antonia Blair 已经回答了这个问题,但我想通过一些基本规则来了解更多。

1:大多数答案都建议使用 .env 文件。我想一次性明确说明。env 不是在这里添加任何安全层。名称所描述的 .env 仅用于在构建时设置环境。例如通过使用环境变量,您可以在构建时设置一些全局值,并且可以在运行时在应用程序中访问这些值。

2:Reactjs 只是一个在客户端浏览器中运行您的 javascript 代码的框架。因此客户端可以完全访问 javascript (Reactjs) 代码。客户端没有什么是安全的。因此,永远不要想通过将所有代码保留在客户端来使某些东西安全或对客户端隐藏。每当您需要向客户端隐藏某些内容时,您需要在服务器端合并某些内容。只有服务器端代码对客户端来说是安全的。

3:所以你要做的是,你将把你的安全密钥保存在服务器端。

假设您的安全密钥的目的是为您的客户制作一个 cookie。所以客户端需要cookie而不是安全密钥,对吗?因此,客户端向服务器请求 cookie,服务器使用安全密钥制作 cookie 并将 cookie 返回给客户端。毕竟客户端只是来吃饼干的,不知道我们怎么做饼干对吧?

4:所以经验法则是,无论您有什么机密数据,都将其保存在服务器上。服务器将使用这些机密数据并将结果数据返回给客户端。

编辑 Sep_06_2021

一位用户要求提供一个编码示例,因此我将提出一个我使用上述技术处理的实时场景。 这是我的用例

  1. 我有一个向非公共 API 提交公共表单的 Reactjs 应用程序。

  2. 非公共 API 是 Harvest API 托管的 https://www.greenhouse.io/

  3. 此 API 需要一个身份验证标头才能使用它发出请求。我已向 API 所有者订阅,并从他们那里收到了一个秘密令牌,我可以将其用于我的请求以访问他们的 API

  4. 当然,我想保持我的令牌个人化,不要将其暴露给 公众用户

  5. 我已经使用 axios 客户端与 API 通信

我有两种方法来执行上述场景

错误的方法

我直接从我的 Reactjs 应用程序向 API 发出请求

下面是我想要访问的 API 端点

apiURL=https://boardsapi.greenhouse.io/v1/boards/xyz/jobs/"+jobId+""

上述 API 端点需要一个授权标头,我将在其中提供我的秘密令牌。

const config = { 
  headers: {
    "Authorization": "Basic ####MySecretCode#####",
  } };  

假设我想用这个请求发布一些表单数据

let formData=MyFormData

我现在可以使用如下所示的 axios 客户端发送我的请求

  let result=await axios.post(apiURL, formData,config);

使用上述技术,我可以成功地将表单数据发布到 Harvest API。

但就像我说的那样,与此 API 进行通信是一种不正确的方式。因为我在客户端暴露了我的秘密令牌。

正确的方法

我在 Nodejs 上构建了一个 API 并公开托管。

假设我想将一些表单数据发布到 Harvest API

let formData=MyFormData

我不会直接从我的客户端应用程序中访问 Harvest API。相反,我已经在我的中间件 API 中公开了端点来处理这个问题。 假设下面是我想要访问的中间件 API 的端点 URL

apiURL=https://proxy-server/apply

上述 API 端点不需要授权标头。所以我可以使用下面的 axios 客户端发送帖子请求

let result=await axios.post(apiURL, formData);

区别很明显。这次我没有在我的请求中提供秘密令牌。因为这不是对 Harvest API 的直接请求,而是对由我开发和托管的中间件 API 的请求。

我在我的中间件 API 中收到此请求,将我的秘密令牌与它一起添加并将其转发到 Harvest API。 Harvest API 的响应返回到我们的 middle_ware API 并因此转发回我们的 Reactjs 客户端应用程序。

秘密令牌现在驻留在我的服务器端 API 中,对外部用户安全。

【讨论】:

  • 根据上面的第 3 点,您知道新开发人员可以用来学习如何做到这一点的任何资源、教程等吗?
  • 完美答案,非技术人员也很容易理解。
  • 您的方法本身存在安全问题:任何人都可以发布到您的中间件端点,并最终在您的示例中使用您的 API 访问收获 API
  • @us_david 这就是我想要的,任何人都可以点击我的 API,我将从收获 API 获取的结果返回给他们。我的 API 对所有人都是公开的。如果我想将我的 API 设为私有,那么就有了 User Authentication 模块,只有经过身份验证的用户才能通过它向我的 API 发出请求
【解决方案6】:

这对我有用:

我在根文件夹中创建了.env。 在那个文件夹中,我添加了我的密钥:

 REACT_APP_API_KEY_YT = "key"
//I added YT for youtube which is where my api key is from

然后我去了.gitignore ||或在您的根目录中创建一个 .gitignore 如果您没有它。在 .gitignore 我添加了 .env

 #api key
 .env

然后我回到了我的应用 js 文件的根目录。对我来说那是 index.js 对于其他它可能是 App.js 在那里我创建了一个 const API_KEY

 const API_KEY =`${process.env.REACT_APP_API_KEY_YT}`

我通过控制台记录它检查了它是否工作。

 console.log("API", API_KEY)

我收到了undefined。 我停止了服务器(Control + C)并重新启动了服务器。 之后,我能够看到钥匙。

【讨论】:

  • .env 文件保存不会像保存 .js 文件那样导致 React 项目重新加载。如果对.env文件进行了更改,您需要CMD+C bpm或yarn star,然后重新启动它。
【解决方案7】:

这是一个在代码中查找 API 密钥的示例,即使您尝试将其隐藏在环境变量中。

我使用 NewsAPI 构建了一个非常简单的应用程序,这需要我注册一个 API 密钥。这是我使用 API 密钥作为环境变量对 NewsAPI 端点的获取。

fetch(`https://newsapi.org/v2/top-headlines?q=${keyword}&apiKey=${process.env.REACT_APP_API_KEY}`)
  .then(res => res.json())
  .then(
    (result) => {
      setIsLoaded(true);
      setArticles(result.articles);
    })

但是,当我使用 Chrome 开发工具检查网络请求时,您将能够看到 API 密钥的实际值。我希望这可以帮助人们了解即使您将 API 密钥存储为环境变量,也有人可以在客户端上找到它。

【讨论】:

  • 根本不回答问题。
  • 这给出了一个示例,说明如何隐藏一个秘密,而不是如何to(或者你是否可以)隐藏一个秘密跨度>
【解决方案8】:

如果您将 API 密钥用于本地开发目的,请将其放在 .env.development 文件下,git 忽略它。 .env 文件中的凭据将由构建过程获取,这将在生产中公开数据。

详情见https://create-react-app.dev/docs/adding-custom-environment-variables/#what-other-env-files-can-be-used

【讨论】:

    【解决方案9】:

    如上所述,创建.env 文件很有帮助。但是需要注意的一点是-

    -如果您在 url 中使用 API_KEY 作为 state 像这样-

    this.state = {          
             url:`http://newsapi.org/v2/everything&apiKey=${process.env.REACT_APP_API_KEY}`
    }
    

    然后它将在 React 开发者工具中可见。

    相反,您可以将 API_KEY 直接放在 fetch 的位置。例如-

    fetch(`http://newsapi.org/v2/everything?&apiKey=${process.env.REACT_APP_API_KEY}`)
    

    【讨论】:

    • 这种变量和它们的值,在包中是可见的吗?我正在搜索 npm run build 生成的静态文件,但没有找到任何相关信息。
    【解决方案10】:

    除了只添加一个.env 文件之外,更进一步的是使用dotenv

    1. 在根文件夹中创建.gitignore 文件。
    2. .gitignore文件中添加.env
    3. 安装dotenv
    4. 需要并配置dotenv
        require('dotenv').config()
    
    1. 在根文件夹中创建.env
    2. 在您的.env 文件中添加特定于环境的变量。确保你有 REACT_APP_... or else it will not work!
        REACT_APP_DB_HOST=localhost
        REACT_APP_DB_USER=root
        REACT_APP_DB_PASS=s1mpl3
    
    1. 在您的应用中使用 process.env 来访问您的 .env 文件中的键和值。
        const db = require('db')
        db.connect({
          host: process.env.DB_HOST,
          username: process.env.DB_USER,
          password: process.env.DB_PASS
        })
    

    【讨论】:

      【解决方案11】:

      不应将安全密钥/秘密发送到客户端。假设您想通过您的应用(在浏览器中运行)从 S3 下载文件到客户端计算机上。

      • 调用服务器以获取临时令牌(随时间过期)

      • 服务器(拥有秘密和一切)制作令牌并将其发回

      • 客户端使用令牌访问 S3

      令牌本身不是秘密,一旦过期就没用了。

      【讨论】:

        猜你喜欢
        • 2021-01-27
        • 2021-04-30
        • 2018-12-07
        • 2017-09-12
        • 2020-04-15
        • 2020-05-12
        • 2022-01-02
        • 1970-01-01
        相关资源
        最近更新 更多