「Node.js」利用 .env 與環境變數隱藏敏感資料 by dotenv


Posted by estella00911 on 2021-08-20

一、大綱

路由:

|-- project
      |-- index.js
      |-- .env
      |-- .sequelizerc
      |-- config
            |-- config.json -> config.js:require 引用 dotenv(在下載 sequelize-cli npm 自動產生)
      |-- migrations
      |-- seeders
      |-- models
            |-- index.js:修改 .json -> .js
      |-- views
      |-- public

  1. 安裝 dotenv
  2. 在專案根目錄新增 .env 檔案,新增環境變數
  3. /project/config/config.json 修改檔案類型與其檔案內容
  4. 在專案根目錄(/project)新增 .sequelizerc 檔案,修改 config 的檔案類型
  5. /project/models/index.js 修改檔案內的文字。
  6. session secret key 放到環境變數。
  7. 測試一下,有沒有出問題。
  8. 重要.gitignore 忽略 .env 檔案

二、使用步驟

1. 安裝 dotenv

1.1 安裝

在專案資料夾的根目錄下 terminal 安裝
npm install --save--dev dotenv

+ dotenv@10.0.0
added 1 package and audited 795 packages in 5.517s

67 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities



   ╭────────────────────────────────────────────────────────────────╮
   │                                                                │
   │      New major version of npm available! 6.14.12 → 7.20.6      │
   │   Changelog: https://github.com/npm/cli/releases/tag/v7.20.6   │
   │               Run npm install -g npm to update!                │
   │                                                                │
   ╰────────────────────────────────────────────────────────────────╯

1.2 檢查有沒有安裝好

npm dotenv --version

$ npm dotenv --version
6.14.12

2. 在專案根目錄新增 .env 檔案,新增環境變數

2.1 新增 .env 檔案

$ pwd
/project
$ touch .env
# 建立好 .env 檔案後,開啟 .env 編輯

2.2 編輯 .env 檔案,新增環境變數

可以將敏感資訊,例如 database 的連結資訊放入 .env 檔案內:
新增並分別令與database 有關的變數為 DB_HOSTDB_USERNAMEDB_PASSWORDDB_PORTDB_DATABASE

// /project/.env
DB_HOST=localhost
DB_USERNAME=user01
DB_PASSWORD=password01
DB_PORT=8080
DB_DATABASE=testdb

SESSION_SECRET: xxxx

3. 在 /project/config/config.json 修改檔案類型與其檔案內容

3.1 原本的檔案(config.json)如下:

// 檔名:config.json
{
 "development": {
    "username": "root",
    "password": null,
    "database": "database_test",
    "host": "127.0.0.1",
    "dialect": "mysql"
  },
  "test": {
    "username": "root",
    "password": null,
    "database": "database_test",
    "host": "127.0.0.1",
    "dialect": "mysql"
  },
  "production": {
    "username": "root",
    "password": null,
    "database": "database_production",
    "host": "127.0.0.1",
    "dialect": "mysql"
  }
}

3.2 修改後的檔案如下:

修改以下行首為加號的地方,並將檔名更為 config.js

// 修改檔名:從 config.json → config.js
+   require('dotenv').config();

+   const config = {
    {
     "development": {
+       "username": process.env.DB_USERNAME,
+       "password": process.env.DB_PASSWORD,
+       "database": process.env.DB_DATABASE,
+       "host": process.env.DB_HOST,
        "dialect": "mysql"
      },
      "test": {
        "username": "root",
        "password": null,
        "database": "database_test",
        "host": "127.0.0.1",
        "dialect": "mysql"
      },
      "production": {
        "username": "root",
        "password": null,
        "database": "database_production",
        "host": "127.0.0.1",
        "dialect": "mysql"
      }
    }
+   module.exports = config;

小結:

  1. 修改加號部分
  2. 修改檔名為 config.js

接下來將檔名更改了,那這樣 sequelize-cli 怎麼找的到 config 檔案,來連結資料庫呢?
就要在以下步驟新增 .sequelizerc 來調整路徑。

4. 在專案根目錄(/project)新增 .sequelizerc 檔案,修改 config 的檔案類型

    'use strict';

    require('dotenv').config();    // don't forget to require dotenv
    const path = require('path');

    module.exports = {
+     // 原本是 config.json,修改成 config.js
+     'config': path.resolve('config', 'config.js'), 
-     'config': path.resolve('config', 'config.json'), 
      'models-path': path.resolve('models'),
      'seeders-path': path.resolve('seeders'),
      'migrations-path': path.resolve('migrations'),
    };

5. 在 /project/models/index.js 修改檔案內的文字。

原先沒有修改這個檔案的內容,結果發現出現以下錯誤:
PS:路徑可以將 /Users/jeanlu/document/github/mentor-program-5th-estella00911/homeworks/week17/hw1/models 看成 /project/models 即可。

Error: Cannot find module 'xxx/project/models/config/config.json'
Require stack:
- xxx/project/models/index.js
- xxx/project/controllers/user.js
- xxx/project/index.js
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:880:15)
    at Function.Module._load (internal/modules/cjs/loader.js:725:27)
    at Module.require (internal/modules/cjs/loader.js:952:19)
    at require (internal/modules/cjs/helpers.js:88:18)
    at Object.<anonymous> (xxx/project/models/index.js:8:16)
    at Module._compile (internal/modules/cjs/loader.js:1063:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1092:10)
    at Module.load (internal/modules/cjs/loader.js:928:32)
    at Function.Module._load (internal/modules/cjs/loader.js:769:14)
    at Module.require (internal/modules/cjs/loader.js:952:19) {
  code: 'MODULE_NOT_FOUND',
  requireStack: [
    'xxx/project/models/index.js',
    'xxx/project/controllers/user.js',
    'xxx/project/index.js'
  ]
}

發現居然跟 /models/index.js 有關,於是查了相關的,發現 config 的路徑不對,就將原本的 const config = require(__dirname + '/../config/config.json')[env]; 改成 const config = require(__dirname + '/../config/config.js')[env];

    'use strict';

    const fs = require('fs');
    const path = require('path');
    const Sequelize = require('sequelize');
    const basename = path.basename(__filename);
    const env = process.env.NODE_ENV || 'development';
+   const config = require(__dirname + '/../config/config.js')[env];
-   const config = require(__dirname + '/../config/config.json')[env];
    const db = {};

再執行一次,就 OK 了!

6. 將 session secret key 放到環境變數。

在 app.js/ index.js 中,要將 sessionsecret key 放到環境變數,並從環境變數中引用:

+   // project/index.js
+   require('dotenv').config(); // 引用 dotenv npm module
    const express = require('express')
    const path = require('path')
    const bodyParser = require('body-parser');
    const flash = require('connect-flash')
    const session = require('express-session')
    const moment = require('moment');
    const shortDateFormat = 'YYYY-MM-DD HH:mm'; //"ddd @ h:mmA"
    const app = express();
    const port = process.env.DB_PORT

    const userController = require('./controllers/user')
    const blogController = require('./controllers/blog')

    app.set('views', './views')
    app.set('view engine', 'ejs')
    app.use('/public', express.static(__dirname + '/public'));

    app.use(bodyParser.json());
    app.use(bodyParser.urlencoded());
    app.use(flash())
    app.use(session({
-      secret: 'xxxxxxxxx'  // 原本的 secret key 是放在 index.js 檔案內的,但這樣是不安全,需要像下面這行一樣,存在環境變數!
+      secret: process.env.SESSION_SECRET,
        resave: false,
        saveUninitialized: true,
        cookie: { maxAge: 60000 }
    }))
   # 略
       app.listen(port, () => {
          console.log(`Example app listening at http://localhost:${port}`)
    })

7. 測試一下,有沒有出問題。

$ node index.js
body-parser deprecated undefined extended: provide extended option index.js:21:20
Example app listening at http://localhost:xxxx

8. 重要.gitignore 忽略 .env 檔案

將敏感資料設置在環境變數裡面,就是不要讓別人知道這些具有隱私的資料,所以當然不能把這些隱私的資料上傳到 GitHub 呀!所以在 push 上 GitHub 之前,要先設定將這個檔案 ignore 掉!
這時候要先移動到根目錄:

$ pwd
xxx/project/
$ ls -al
drwxr-xr-x   14 jeanlu  staff    448  8 20 12:05 .
drwxr-xr-x    7 jeanlu  staff    224  8 14 13:50 ..
-rw-r--r--@   1 jeanlu  staff  10244  8 19 09:08 .DS_Store
-rw-r--r--    1 jeanlu  staff    121  8 20 11:52 .env
-rw-r--r--    1 jeanlu  staff    314  8 19 18:41 .sequelizerc
drwxr-xr-x    3 jeanlu  staff     96  8 20 09:34 config
drwxr-xr-x    4 jeanlu  staff    128  8 13 17:48 controllers
-rw-r--r--@   1 jeanlu  staff   2071  8 20 12:16 index.js
drwxr-xr-x    5 jeanlu  staff    160  8 16 19:33 migrations
drwxr-xr-x    6 jeanlu  staff    192  8 16 19:33 models
drwxr-xr-x    5 jeanlu  staff    160  8 19 09:10 public
drwxr-xr-x    3 jeanlu  staff     96  8 17 14:59 scripts
drwxr-xr-x    2 jeanlu  staff     64  8 15 13:59 seeders
drwxr-xr-x   12 jeanlu  staff    384  8 17 18:12 views
-rw-r--r--    1 jeanlu  staff  6261697  7 19 00:24 .eslintcache
-rw-r--r--    1 jeanlu  staff       70  7 26 21:15 .eslintignore
-rw-r--r--    1 jeanlu  staff      348  4 26 14:06 .eslintrc.js
drwxr-xr-x   15 jeanlu  staff      480  8 19 18:42 .git
-rw-r--r--    1 jeanlu  staff       51  8 19 18:29 .gitignore
drwxr-xr-x    5 jeanlu  staff      160  4 26 14:08 .husky
-rw-r--r--    1 jeanlu  staff    55234  4 26 14:06 README.md
drwxr-xr-x  518 jeanlu  staff    16576  8 19 18:20 node_modules
-rw-r--r--    1 jeanlu  staff   278808  8 19 18:20 package-lock.json
-rw-r--r--    1 jeanlu  staff     1355  8 19 18:20 package.json
$ vim .gitignore

使用 vim 編輯器,打開 .gitignore,加上 .env,這樣在 push 至 GitHub 時,就不會 push 到具有敏感資料的 .env

    node_modules/
    conn.php
    .DS_STORE
    .eslintcache
+   .env

參考:
dotenv - npm
Sequelize Migrate Not Recognising Dotenv - stackoverflow
Importance of session secret key in Express web framework - semicolonworld


#dotenv #env #nodejs #Express #Sequelize







Related Posts

Vue.js 學習旅程Mile 7 – 資料屬性綁定篇:v-bind

Vue.js 學習旅程Mile 7 – 資料屬性綁定篇:v-bind

TechBridge Weekly 謝幕感想

TechBridge Weekly 謝幕感想

F2E合作社|select下拉選單|Bootstrap 5網頁框架開發入門

F2E合作社|select下拉選單|Bootstrap 5網頁框架開發入門


Comments