node中的密碼安全

本文將講解對於先後端分離的項目,前端註冊或登陸時如何保證用戶密碼安全傳輸到server端,最終存入數據庫

爲何須要加密

加密真的有必要嗎?
咱們先來看一看前端發起的ajax請求中,若是不對密碼進行加密,會發生什麼。
f12打開chrome開發者工具,找到請求,查看請求參數以下:css

clipboard.png

若是你的協議是http,那麼前端傳給後端的密碼差很少是裸奔狀態,由於http傳輸的是明文,極可能在傳輸過程當中被竊聽,假裝或篡改。
那麼,弄個https不就行了嗎?
https的確可以極大增長網站的安全性,可是用https得先買證書(也有免費的),對於我的站點或者不想弄證書的狀況下,那最起碼也得對用戶密碼進行一下加密吧。前端

流程圖

先看一下大致流程圖,首先,咱們用工具生成公鑰和私鑰,將其放入server端,前端發起請求獲取公鑰,拿到公鑰後對密碼進行加密,而後將加密後的密碼發送到server端,server端將用密鑰解密,最後再用sha1加密密碼,存入數據庫。
圖片描述node

生成RSA公鑰和密鑰

既然選擇RSA加密,那麼首先得有工具啊,常見的有openssl,但這裏不介紹,感興趣的請自行查閱,對於node而言,我介紹一個不錯的庫Node-RSA,咱們將用它來生成RSA公鑰和密鑰。ios

RSA是一種非對稱加密算法,即由一個密鑰和一個公鑰構成的密鑰對,經過密鑰加密,公鑰解密,或者經過公鑰加密,密鑰解密。其中,公鑰能夠公開,密鑰必須保密git

用Node-RSA生成的公鑰和密鑰代碼以下:github

const NodeRSA = require('node-rsa')
const fs = require('fs')

// Generate new 512bit-length key
var key = new NodeRSA({b: 512})
key.setOptions({encryptionScheme: 'pkcs1'})

var privatePem = key.exportKey('pkcs1-private-pem')
var publicDer = key.exportKey('pkcs8-public-der')
var publicDerStr = publicDer.toString('base64')

// 保存返回到前端的公鑰
fs.writeFile('./pem/public.pem', publicDerStr, (err) => {
  if (err) throw err
  console.log('公鑰已保存!')
})
// 保存私鑰
fs.writeFile('./pem/private.pem', privatePem, (err) => {
  if (err) throw err
  console.log('私鑰已保存!')
})

執行完成後,咱們將在根目錄下獲得公鑰和私鑰文件:ajax

clipboard.png

注意:server端的公鑰和密鑰應該隔一段時間換一次,好比每次服務器重啓時。算法

前端加密

核心代碼以下:chrome

<script src="https://cdn.bootcss.com/jsencrypt/2.3.1/jsencrypt.min.js"></script>
  <script src="https://cdn.bootcss.com/axios/0.18.0/axios.min.js"></script>
  <script>
    function reg() {
      axios({
        method: 'post',
        url: 'http://127.0.0.1:3000/getPublicKey'
      })
        .then(res => {
          let result = res.data

          // 從後端獲取的公鑰 String
          var publicPem = result
          // 用JSEncrypt對密碼進行加密
          var encrypt = new JSEncrypt()
          encrypt.setPublicKey(publicPem)
          var password = 'abc123'
          password = encrypt.encrypt(password)

          axios({
            method: 'post',
            url: 'http://127.0.0.1:3000/reg',
            data: {
              password: password
            }
          })
            .then(res => {
              let result = res.data
              console.log(result)
            })
            .catch(error => {
              console.log(error)
            })
        })
    }
  </script>

前端將用到jsencrypt對其進行加密,詳細用法請參考github。數據庫

後端解密

後端核心代碼:

const express = require('express');
const crypto = require('crypto');
const fs = require('fs');

var privatePem = fs.readFileSync('./pem/private.pem');

var app = express();
app.use(express.json());

// CORS 注意:要放在處理路由前
function crossDomain(req, res, next) {
  res.header('Access-Control-Allow-Origin', '*');
  res.header('Access-Control-Allow-Headers', 'Content-Type');

  next();
}
app.use(crossDomain)

app.use(function (req, res, next) {
  // 不加會報錯
  if (req.method === 'OPTIONS') {
    res.end('ok')
    return
  }

  switch (req.url) {
    case '/getPublicKey':
      let publicPem = fs.readFileSync('./pem/public.pem', 'utf-8')
      res.json(publicPem)
      break
    case '/reg':
      // 解密
      var privateKey = fs.readFileSync('./pem/private.pem', 'utf8')
      var password = req.body.password
      var buffer2 = Buffer.from(password, 'base64')
      var decrypted = crypto.privateDecrypt(
        {
          key: privateKey,
          padding: crypto.constants.RSA_PKCS1_PADDING // 注意這裏的常量值要設置爲RSA_PKCS1_PADDING
        },
        buffer2
      )
      console.log(decrypted.toString('utf8'))

      // sha1加密
      var sha1 = crypto.createHash('sha1');
      var password = sha1.update(decrypted).digest('hex');
      console.log('輸入到數據庫中的密碼是: ', password)
      // 存入數據庫中
      // store to db...
      res.end('reg ok')
      break
  }
})

app.listen(3000, '127.0.0.1')

這裏,我是用node自帶模塊crpto進行解密,固然,你也能夠用Node-RSA的方法進行解密。

最後

咱們再來看一看前端請求的密碼信息:

clipboard.png

這樣一串字符,即使被他人獲取,若是沒有密鑰,在必定程度上,他是沒法知道你的密碼的。

固然,關於網絡安全是一個大話題,本篇只是對其中的一小部分進行介紹,歡迎留言討論,但願對您有幫助。

相關文章
相關標籤/搜索