nodejs 前後端數據加密方案

nodejs 前後端數據加密方案

前言

即時沒有深入研究過密碼學,但是對於數據加密,我想應該很多同學都有所接觸吧。

雖然對於前端來說,其實數據加密的意義並不大。你代碼都給人家了,只要有技術,有耐心,破解就只是時間的問題了。

在對待前端加密的態度上面,我始終秉承着一句話:“防小人,不防君子”。

但是奈何,很多時候,你不想做這麼白費心力的事情,但是老闆不答應。

我本將心向明月,奈何明月照溝渠。

很多時候,在工作中,都是人在江湖,身不由己!

之前在工作中曾經有這方面的需求,因此稍微研究了下。

如何加密

對於加密的情形,我們需要區分好幾種情況,比如是對數據進行加密呢,還是對服務器請求地址的 path 進行加密。

雖然應用的情況不同,但是解決的思路都是大同小異的。

最初級的方案當然是,自己寫個簡單的加密算法,比如數據是 “1234567890”,我做一個映射,映射到 ‘0987654321’,將順序打亂,一般就很難看出數據的原格式是什麼了,除非別人知道我的映射規則。

上面的加密方案几乎是所有數據加密的基礎原理,毫不誇張的說,無出其右。

但是我們的初級方案有一個致命的缺點,它太基礎了,我們只是考慮到了數據中數字的情況,但是一旦數據中出現別的字符,比如中文、英文字符、希臘字母,我們上面的映射方法就抓瞎了。

我們當然可以進一步對我們的數據映射規則進行擴充,但是這個構成前人早就給我們做好了,因爲我們大可不必自己衝頭開始造這麼基礎的輪子。

這就跟蓋房子一樣,我們可以從燒製磚塊開始幹,也可以選擇購買成品磚塊,加快我們的建造速度。

所以,現在我們應該明白了,加密的關鍵是要有一套好的加密算法,我們直接拿來用就行了。

常見的加密算法有:DES加密算法、AES加密算法、RSA加密算法、Base64加密算法、MD5加密算法、SHA1加密算法、XXXTEA加密算法等等。

對於這些算法,感興趣的同學,可以自己查找資料,去深入的學習其原理。

當然,對其我們只需要有個基本的瞭解就行了,不推薦太過深入,甚至於想自己去實現一套。

有這個研究的想法當然是好的,但是不推薦具體去行動,不僅費時還費心力健壯性還不行,等你研究好了,老闆可能就請你打包離開公司了。

況且,前端本身就有非常成熟的開源工具:crypto-js,我們直接拿來用就行了。

對應的 nodejs 後端,當然也可以用這個工具包,但是更好的選擇是採用 nodejs 原生支持的 crypto,用法其實也大同小異。

後端(nodejs)

比如我們想採用 aes-128-cbc 加密算法,可以像下面這和麼用:

/**
 * 加密
 * @param {string} data 原文
 * @param {string} key CipherKey
 * @param {string} iv
 */
const aesEncrypt = (data, key = global._config.secretKey, iv = global._config.secretIv) => {
  const cipher = crypto.createCipheriv('aes-128-cbc', key, iv);
  let crypted = cipher.update(data, 'utf8', 'hex');
  crypted += cipher.final('hex');
  return crypted;
};

/**
 * 解密
 * @param {string} encrypted 密文
 * @param {string} key CipherKey
 * @param {string} iv
 */
const aesDecrypt = (encrypted, key = global._config.secretKey, iv = global._config.secretIv) => {
  const decipher = crypto.createDecipheriv('aes-128-cbc', key, iv);
  let decrypted = decipher.update(encrypted, 'hex', 'utf8');
  decrypted += decipher.final('utf8');
  return decrypted;
};

封裝一個加密的方法,一個解密的方法,當然密鑰必須一樣。

爲了使安全性更高,這裏我們選擇採用隨機生成 key 和 iv,而前端可以在初始化的時候,發請求向後端請求,拿到後端服務器每次運行的時候動態生成的 key 和 iv。

如果想用別的加密算法,可以瞭解下 nodejs 的 api:

前端

其實前後端是相互獨立的,但是你必須得採用相同的加密算法,我們這裏就以 aes-128-cbc 加密算法來作爲例子。

當然,我們採用的就是前面說的 crypto-js

我們同樣需要對加密和解密進行封裝,而 key 和 iv,我們都需要從服務器進行獲取。

/**
 * 解密文本
 * @name decryptText
 * @function
 * @param {string} encryptText 需要解密的文本
 * @returns Promise
 */
export const decryptText = async (encryptText) => {
  /** 如果沒有密鑰,需要先從服務器獲取密鑰 */
  if (secretKey === undefined && secretIv === undefined) {
    try {
      await obtainKeyFromServer();
    } catch (err) {
      throw new Error(err);
    }
  }

  let text = AES.decrypt(encryptText, CryptoJS.enc.Utf8.parse(secretKey), {
    iv: CryptoJS.enc.Utf8.parse(secretIv),
    mode: CryptoJS.mode.CBC,
    format: CryptoJS.format.Hex,
  }).toString(CryptoJS.enc.Utf8);
  try {
    text = JSON.parse(text);
  } catch (err) {
    console.log(err);
  }

  return text;
};

/**
 * 加密文本
 * @name encryptText
 * @function
 * @param {string | object | array} text 需要加密的文本
 */
export const encryptText = async (text) => {
  /** 如果沒有密鑰,需要先從服務器獲取密鑰 */
  if (secretKey === undefined && secretIv === undefined) {
    try {
      await obtainKeyFromServer();
    } catch (err) {
      throw new Error(err);
    }
  }

  if (!isString(text)) text = JSON.stringify(text);

  return AES.encrypt(text, CryptoJS.enc.Utf8.parse(secretKey), {
    iv: CryptoJS.enc.Utf8.parse(secretIv),
    mode: CryptoJS.mode.CBC,
    format: CryptoJS.format.Hex,
  }).toString();
};

加密 url

如果只是想加密數據,直接調用上面封裝好的幾個方法進行處理就好了。

但是有時候,老闆讓你對url也進行加密處理,你該怎麼搞呢。

其實原理是一樣的,無非加密的對象由數據變成了 url 了而已。

這裏面有幾個關鍵的地方,值得我們注意的。

前端,我們可以通過 window.location 對象,獲取到 href,然後從裏面拿到 host, port, path 以及 params 等等喫參數。

我們來分析一下,一個合格的 url 的格式一般是這樣的:
image.png

主機和端口號我們肯定加密不了,所以只有在 path 和 params 上下功夫了。

在進行加密的時候,需要注意的地方是,我們要對 path 和 params 分開進行加密,因爲後端需要對加密的 path 進行解密,用以區分我們發送的是什麼請求。而 params 一般是我們在發送 get 請求的時候,攜帶的信息,所以需要單獨進行加密。

後記

其實,前後端加密數據,意義並沒有那麼大,只要有心人想要破解,還是沒有太大難度的。

區別在於,你用的加密算法越複雜,你就需要花費更多的時間去執行加密、解密操作。

而保密的最關鍵點在於,不能泄漏了密鑰和算法,否則數據就等同於不設防了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章