如何在 create-react-app 中隐藏 API 键?

How do I hide API key in create-react-app?

我在 create-react-app 中做了一个天气应用。如何隐藏 API 键以便我可以提交 GitHub?

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

警告

除非您正在制作教程应用程序,否则不要在客户端源代码(例如 React 应用程序)中放置诸如 API 密钥之类的秘密。 From Create React App's documentation:

WARNING: Do not store any secrets (such as private API keys) in your React app!

Environment variables are embedded into the build, meaning anyone can view them by inspecting your app's files.

首先,在项目的根目录中创建一个 .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 后读取它们,请重新启动您的服务器。

免责声明

WARNING: Do not store any secrets (such as private API keys) in your React app!

Environment variables are embedded into the build, meaning anyone can view them by inspecting your app's files.

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

以下是对我有用的方法:

我在根文件夹中创建了 .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) 并重新启动了服务器。 之后我就能看到钥匙了。

来自反应 documentation:

WARNING: Do not store any secrets (such as private API keys) in your React app!

Environment variables are embedded into the build, meaning anyone can view them by inspecting your app's files.

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

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

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

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

如上所述,创建 .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}`)

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

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

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

3:所以你要做的是,你将把你的安全密钥保存在 server-side。

假设您的安全密钥的目的是为您的客户制作一个 cookie。所以客户需要 cookie 而不是安全密钥,对吗?因此 client-side 向服务器请求 cookie,服务器使用安全密钥生成 cookie,然后 return 将 cookie 发送给客户端。毕竟客户只是来吃饼干的,不知道我们怎么做饼干才对?

4:所以经验法则是,只要有机密数据,就将其保存在服务器上。服务器将使用此机密数据和 return 向客户端公开的结果数据。

编辑Sep_06_2021

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

  1. 我有一个 Reactjs 应用程序,它向非 public API.

    提交 public 表单
  2. 非publicAPI是收获API主持 https://www.greenhouse.io/

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

  4. 当然我想把我的令牌保密,不要暴露给别人 public 用户

  5. 我用axios客户端与API

    通信

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

不正确的方法

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

让我们说下面是我想要点击的API端点

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

上面的 API 端点需要授权 Header,我将在其中提供我的秘密令牌。

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

假设我想post一些表单数据与这个请求

let formData=MyFormData

我现在可以像下面这样使用 axios 客户端发送我的请求

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

使用上述技术我可以成功地post 我的表单数据收获API。

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

正确的方法

我在 Nodejs 上构建了一个 API 并 public 托管了它。

假设我想post一些表单数据来收获API

let formData=MyFormData

我不会直接从我的客户端 application.And 访问 Harvest API,而是我在中间件 API 中公开了端点来处理这个问题。 让我们说下面是我的中间件API的端点URL,我想点击

apiURL=https://proxy-server/apply

上述 API 端点不需要授权 Header。所以我可以使用 axios 客户端发送 post 请求,如下所示

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

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

我在我的 middle-ware API 中收到此请求,添加我的秘密令牌并将其转发给 Harvest API。来自 Harvest API 的响应被 returned 到我们的 middle_ware API 并因此转发回我们的 Reactjs 客户端应用程序。

秘密令牌现在位于我的 server-side API 上并且对外部用户是安全的。

这是一个在代码中找到 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 密钥存储为环境变量,也有人可以在客户端上找到它。

编辑以解释如何隐藏API键: 您可以从服务器端逻辑发出 HTTP 请求,这样您就可以安全地隐藏 .env 文件中的 API 密钥。在下面的示例中,我创建了一个指向 /top-headlines/:searchTerm 的指向。一旦收到对该端点的请求,然后我使用存储在我的 .env 文件中的“NEWS_API_KEY”环境变量将 axios 请求发送到新闻 API。

route.get("/top-headlines/:searchTerm", async (req, res) => {
      const { searchTerm } = req.params;
    
      try {
        const articlesResponse = await axios.get(
          `https://newsapi.org/v2/top-headlines?q=${searchTerm}&apiKey=${process.env.NEWS_API_KEY}`
        );
    
        const articlesData = articlesResponse.data;
    
        res.send(articlesData);
      } catch (error) {
        console.log(error);
      }
    });

除了仅添加一个 .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
    })

安全的 key/secret 绝不能发送到客户端。比如说,您想通过您的应用程序(在浏览器中 运行)从客户端计算机上的 S3 下载文件。

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

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

  • 客户端使用令牌访问S3

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