# Lesson 7 - Hash password, ENV

## Hash password

### Tổng quan

Hashing (băm) là quá trình biến đổi một chuỗi dữ liệu (thường là mật khẩu) thành một chuỗi giá trị băm (hash),  một chuỗi mã hóa duy nhất không thể đảo ngược về dữ liệu gốc.

Được sử dụng để bảo mật mật khẩu trong cơ sở dữ liệu. Thay vì lưu trữ mật khẩu dạng văn bản, chúng ta chỉ lưu trữ giá trị hash của mật khẩu.

Điều này sẽ ngăn chặn tấn công từ việc thu thập mật khẩu người dùng, sử dụng để đăng nhập hệ thống.

Ví dụ:

Ta có một chuỗi "mypassword123", với quá trình hashing như sau:

"mypassword123" + 'salt' =>"6e659deaa85842cdabb5c6305fcc40033ba43772ec00d45c2a3c921741a5e377"

{% hint style="info" %}

* Salt là chuỗi ký tự ngẫu nhiên duy nhất kết hợp mật khẩu để sản sinh ra chuỗi mã hoá.
* Với mỗi một salt, cùng 1 thuật toán, sẽ chỉ cho ra một chuỗi hash với giá trị đầu vào
  {% endhint %}

### Bcrypt

Với NodeJs, ta sẽ có rất nhiều các thư viện, thậm chí là module có sẵn cung cấp các thuật toán, để ứng dụng trong việc mã hoá mật khẩu, thư viện bcrypt sẽ là một lựa chọn thích hợp bởi sự đơn giản, cũng như hiệu quả đem lại.

Để cài đặt thư viện, ta sẽ dùng câu lệnh:

```
npm i bcrypt
```

Thực hiện hashing:

```javascript
import express from 'express';
import bcrypt from 'bcrypt';

const app = express();

const saltRounds = 10;
app.use(express.json());

app.post('/register', (req, res) => {
  const {email, password } = req.body;
  // tạo chuỗi ngẫu nhiên
  const salt = bcrypt.genSaltSync(saltRounds);
  // thực hiện mã hoá với chuỗi salt
  const hash = bcrypt.hashSync(password, salt);
  
  // code insert account to model here...
  
  res.status(201).send({
    message:'Register with hash password!',
    email,
    hash,
    salt
  });
  
});

app.listen(8080, () => {
  console.log(`Server is running on port ${port}`);
});

```

* Khi lưu thông tin, với mật khẩu, ta sẽ lưu mật khẩu đã được mã hoá.
* Bắt buộc phải lưu chuỗi salt.
* Để sử dụng đăng nhập, logic đơn giản để kiểm tra mật khẩu, ta sẽ mã hoá chuỗi gửi lên một lần nữa và so sánh với mật khẩu đã được lưu trước đó.

Ví dụ Đăng nhập

```javascript
app.post('/login', async (req, res) => {
  const {email, password } = req.body;
  // tìm thông tin user | tài khoản với email được gửi lên
  const currentUser = await UserModel.findOne({email});
  if(!currentUser) throw new Error("Sai tài khoản hoặc mật khẩu");
  
  const hashingPasswordLogin = bcrypt.hashSync(password, currentUser.salt);
  // compare password
  if(hashingPasswordLogin !== currentUser.password) throw new Error("Sai tài khoản hoặc mật khẩu");
  
  res.status(201).send({
    message:'Login successfully!',
    email,
    // v.v user info
  });
  
});
```

Trong trường hợp đổi mật khẩu, ta vẫn sẽ áp dụng logic giống như đăng ký và nên cập nhật lại chuỗi salt mới.

> Có thể tìm hiểu thêm về thư viện, để sử dụng hàm so sánh của bcrypt.

## ENV

Env viết tắt của Environment trong Environment variables (biến môi trường), giúp lưu trữ các giá trị, thông qua biến với file env, các giá trị này không thuộc vào resource của project, mà thuộc về môi trường chạy (máy chủ) của server đang được chạy.

Sử dụng env sẽ giúp bảo mật các thông tin quan trọng, cần được sử dụng trực tiếp trên ứng dụng, nhưng không lưu trữ trong resource của dự án (mã nguồn).

File này sẽ không được public trên các nền tảng quản trị mã nguồn, ví dụ: github, gitlab,...

Để sử dụng env trong nodejs, ta sẽ sử dụng thư viện `dotenv`. Cài đặt với cú pháp:

```
npm i dotenv
```

Tạo một file tên `.env` trong thư mục gốc của dự án và đặt các biến môi trường trong đó:

```
PORT=8080
DATABASE_URL=mongodb://localhost:27017/mindx-webfullstack
```

Sử dụng các biến tại file env:

`index.js`

```javascript
import express from 'express';
import mongoose from 'mongoose';
// sử dụng thư viện dotenv để có thể đọc giá trị trong file môi trường (.env)
import dotenv from 'dotenv';
dotenv.config();
// cú pháp để lấy giá trị: process.env.{tên giá trị}

mongoose.connect(process.env.DATABASE_URL);
const app = express();

app.use(express.json());

app.listen(process.env.PORT || 3000, () => {
  console.log(`Server is running on port ${port}`);
});

```

{% hint style="info" %}

* Cần phải khởi chạy dotenv.config() với phạm vi global (tức câu lệnh này phải ở trước các câu lệnh dùng process.env) để được nạp các biến môi trường.
* **Luôn** phải chủ động ngắt và restart lại server mỗi khi có sự thay đổi từ file env.
  {% endhint %}

### Các dạng file môi trường

Trong thực tế, khi triển trai một sản phẩm, ta sẽ có nhiều loại môi trường triển khai, chính vì thế, với env này, ta cũng sẽ có nhiều loại file env.

Ví dụ một số dạng file môi trường

1. `.env.example`

   File này sẽ lưu trữ chủ yểu là các giá trị của môi trường có thể cần sử dụng, mục đích là để hướng dẫn map các giá trị tương ứng với các biến được khai báo. Ví dụ:
2. `.env.staging`

   Dùng cho môi trường thử nghiệm hoặc đánh giá trước khi triển khai. Bạn có thể sử dụng nó để cấu hình môi trường thử nghiệm trước khi triển khai lên môi trường sản phẩm chính.&#x20;
3. `.env.test`

   Dành cho môi trường kiểm tra và tự động hóa kiểm tra (testing) của ứng dụng. Điều này giúp bạn cấu hình môi trường kiểm tra để chạy các bài kiểm tra mà không ảnh hưởng đến cơ sở dữ liệu và tài nguyên chính của sản phẩm.
4. `.env.production`

   Dùng để cấu hình các biến môi trường cho môi trường sản phẩm (production). Đây là nơi bạn đặt tất cả các giá trị cần thiết cho sản phẩm của bạn, bao gồm các khóa bí mật, cổng, đường dẫn cơ sở dữ liệu chính, v.v.

{% hint style="info" %}
Ta chỉ có thể dùng file .env để sử dụng các giá trị môi trường, tất cả các dạng file khác, chỉ cần copy dữ liệu và paste vào file .env
{% endhint %}

## Kết

Trên đây là toàn bộ nội dung của bài học, mục đích chính là để làm quen, tư duy về các nghiệp vụ bảo mật cơ bản nhất trong việc lập trình server. Trong nội dung bài tiếp theo, ta sẽ dành thời gian, tìm hiểu, ứng dụng với token.
