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 请求的时候,携带的信息,所以需要单独进行加密。

后记

其实,前后端加密数据,意义并没有那么大,只要有心人想要破解,还是没有太大难度的。

区别在于,你用的加密算法越复杂,你就需要花费更多的时间去执行加密、解密操作。

而保密的最关键点在于,不能泄漏了密钥和算法,否则数据就等同于不设防了。

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