73 篇原创好文~
本文首发于政采云前端团队博客:敏感数据加密计划及实现

前言

当初是大数据时代,须要收集大量的个人信息用于统计。一方面它给咱们带来了便当,另一方面一些个人信息数据在无意间被泄露,被非法分子用于采购和彩色产业。

2018 年 5 月 25 日,欧盟曾经强制执行《通用数据保护条例》(General Data Protection Regulation,缩写作 GDPR)。该条例是欧盟法律中对所有欧盟集体对于数据保护和隐衷的标准。这意味着集体数据必须应用假名化或匿名化进行存储,并且默认应用尽可能最高的隐衷设置,以防止数据泄露。

置信大家也都不想让本人在里面“裸奔”。所以,作为前端开发人员也应该尽量避免用户集体数据的明文传输,尽可能的升高信息泄露的危险。

看到这里可能有人会说当初都用 HTTPS 了,数据在传输过程中是加密的,前端就不须要加密了。其实不然,我能够在你发送 HTTPS 申请之前,通过谷歌插件来捕捉 HTTPS 申请中的个人信息,上面我会为此演示。所以前端数据加密还是很有必要的。

数据泄露形式

  • 中间人攻打

    中间人攻打是常见的攻击方式。具体过程能够参见这里。大略的过程是中间人通过 DNS 坑骗等伎俩劫持了客户端与服务端的会话。

    客户端、服务端之间的信息都会通过中间人,中间人能够获取和转发两者的信息。在 HTTP 下,前端数据加密还是防止不了数据泄露,因为中间人能够伪造密钥。为了防止中间人攻打,咱们个别采纳 HTTPS 的模式传输。

  • 谷歌插件

    HTTPS 尽管能够避免数据在网络传输过程中被劫持,然而在发送 HTTPS 之前,数据还是能够从谷歌插件中泄露进来。

    因为谷歌插件能够捕捉 Network 中的所有申请,所以如果某些插件中有歹意的代码还是能够获取到用户信息的,上面为大家演示。

    所以光采纳 HTTPS,一些敏感信息如果还是以明文的模式传输的话,也是不平安的。如果在 HTTPS 的根底上再进行数据的加密,那相对来说就更好了。

加密算法介绍

  • 对称加密

    对称加密算法,又称为共享密钥加密算法。在对称加密算法中,应用的密钥只有一个,发送和接管单方都应用这个密钥对数据进行加密和解密。

    这就要求加密和解密方当时都必须晓得加密的密钥。其长处是算法公开、计算量小、加密速度快、加密效率高;毛病是密钥泄露之后,数据就会被破解。个别不举荐独自应用。依据实现机制的不同,常见的算法次要有AES、ChaCha20、3DES等。

  • 非对称加密

    非对称加密算法,又称为公开密钥加密算法。它须要两个密钥,一个称为公开密钥 (public key),即公钥;另一个称为公有密钥 (private key),即私钥。

    他俩是配对生成的,就像钥匙和锁的关系。因为加密和解密应用的是两个不同的密钥,所以这种算法称为非对称加密算法。其长处是算法强度简单、安全性高;毛病是加解密速度没有对称加密算法快。常见的算法次要有RSA、Elgamal等。

  • 散列算法

    散列算法又称散列函数、哈希函数,是把音讯或数据压缩成摘要,使得数据质变小,将数据的格局固定成特定长度的值。个别用于校验数据的完整性,平时咱们下载文件就能够校验 MD5 来判断下载的数据是否残缺。常见的算法次要有 MD4、MD5、SHA 等。

实现计划

  • 计划一:如果用对称加密,那么服务端和客户端都必须晓得密钥才行。那服务端势必要把密钥发送给客户端,这个过程中是不平安的,所以单单用对称加密行不通。
  • 计划二:如果用非对称加密,客户端的数据通过公钥加密,服务端通过私钥解密,客户端发送数据实现加密没问题。客户端承受数据,须要服务端用公钥加密,而后客户端用私钥解密。所以这个计划须要两套公钥和私钥,须要在客户端和服务端各自生成本人的密钥。

  • 计划三:如果把对称加密和非对称加密相结合。客户端须要生成一个对称加密的密钥 1,传输内容与该密钥 1进行对称加密传给服务端,并且把密钥 1 和公钥进行非对称加密,而后也传给服务端。服务端通过私钥把对称加密的密钥 1 解密进去,而后通过该密钥 1 解密出内容。以上是客户端到服务端的过程。如果是服务端要发数据到客户端,就须要把响应数据跟对称加密的密钥 1 进行加密,而后客户端接管到密文,通过客户端的密钥 1进行解密,从而实现加密传输。

  • 总结:以上只是列举了常见的加密计划。总的来看,计划二比较简单,然而须要保护两套公钥和私钥,当公钥变动的时候,必须告诉对方,灵活性比拟差。计划三绝对计划二来说,密钥 1 随时能够变动,并且不须要告诉服务端,相对来说灵活性、安全性好点并且计划三对内容是对称加密,当数据量大时,对称加密的速度会比非对称加密快。所以本文采纳计划三给予代码实现。

代码实现

  • 上面是具体的代码实现(以登录接口为例),次要的目标就是要把明文的个人信息转成密文传输。其中对称加密库应用的是 AES,非对称加密库应用的是RSA。
  • 客户端:

    • AES 库(aes-js):https://github.com/ricmoo/aes-js
    • RSA库(jsencrypt):https://github.com/travist/js...
    • 具体代码实现登录接口

      • 客户端须要随机生成一个 aesKey,在页面加载完的时候须要从服务端申请 publicKey

        let aesKey = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; // 随机产生let publicKey = ""; // 公钥会从服务端获取// 页面加载完之后,就去获取公钥window.onload = () => {  axios({    method: "GET",    headers: { "content-type": "application/x-www-form-urlencoded" },    url: "http://localhost:3000/getPub",  })    .then(function (result) {      publicKey = result.data.data; // 获取公钥    })    .catch(function (error) {      console.log(error);    });};
      • aes加密和解密办法

        /** * aes加密办法 * @param {string} text 待加密的字符串
   */  function aesEncrypt(text, key) {    const textBytes = aesjs.utils.utf8.toBytes(text); // 把字符串转换成二进制数据      // 这边应用CTR-Counter加密模式,还有其余模式能够抉择,具体能够参考aes加密库    const aesCtr = new aesjs.ModeOfOperation.ctr(key, new aesjs.Counter(5));      const encryptedBytes = aesCtr.encrypt(textBytes); // 进行加密    const encryptedHex = aesjs.utils.hex.fromBytes(encryptedBytes); // 把二进制数据转成十六进制      return encryptedHex;  }    /**   * aes解密办法   * @param {string} encryptedHex 加密的字符串   * @param {array} key 加密key   */  function aesDecrypt(encryptedHex, key) {    const encryptedBytes = aesjs.utils.hex.toBytes(encryptedHex); // 把十六进制数据转成二进制    const aesCtr = new aesjs.ModeOfOperation.ctr(key, new aesjs.Counter(5));      const decryptedBytes = aesCtr.decrypt(encryptedBytes); // 进行解密    const decryptedText = aesjs.utils.utf8.fromBytes(decryptedBytes); // 把二进制数据转成utf-8字符串      return decryptedText;  }  ```- 申请登录      ```javascript    /**     * 登陆接口     */    function submitFn() {      const userName = document.querySelector("#userName").value;      const password = document.querySelector("#password").value;      const data = {        userName,        password,      };          const text = JSON.stringify(data);      const sendData = aesEncrypt(text, aesKey); // 把要发送的数据转成字符串进行加密      console.log("发送数据", text);          const encrypt = new JSEncrypt();      encrypt.setPublicKey(publicKey);      const encrypted = encrypt.encrypt(aesKey.toString()); // 把aesKey进行非对称加密          const url = "http://localhost:3000/login";      const params = { id: 0, data: { param1: sendData, param2: encrypted } };          axios({        method: "POST",        headers: { "content-type": "application/x-www-form-urlencoded" },        url: url,        data: JSON.stringify(params),      })        .then(function (result) {          const reciveData = aesDecrypt(result.data.data, aesKey); // 用aesKey进行解密          console.log("接收数据", reciveData);        })        .catch(function (error) {          console.log("error", error);        });    }    ```
  • 服务端(Node):

    • AES库(aes-js):https://github.com/ricmoo/aes-js
    • RSA 库(node-rsa):https://github.com/rzcoder/no...
    • 具体代码实现登录接口

      • 援用加密库

        const http = require("http");const aesjs = require("aes-js");const NodeRSA = require("node-rsa");const rsaKey = new NodeRSA({ b: 1024 }); // key的size为1024位let aesKey = null; // 用于保留客户端的aesKeylet privateKey = ""; // 用于保留服务端的公钥rsaKey.setOptions({ encryptionScheme: "pkcs1" }); // 设置加密模式
      • 实现login接口

        http  .createServer((request, response) => {    response.setHeader("Access-Control-Allow-Origin", "*");    response.setHeader("Access-Control-Allow-Headers", "Content-Type");    response.setHeader("Content-Type", "application/json");    switch (request.method) {      case "GET":        if (request.url === "/getPub") {          const publicKey = rsaKey.exportKey("public");          privateKey = rsaKey.exportKey("private");          response.writeHead(200);          response.end(JSON.stringify({ result: true, data: publicKey })); // 把公钥发送给客户端          return;        }        break;      case "POST":        if (request.url === "/login") {          let str = "";          request.on("data", function (chunk) {            str += chunk;          });          request.on("end", function () {            const params = JSON.parse(str);            const reciveData = decrypt(params.data);            console.log("reciveData", reciveData);            // 一系列解决之后            response.writeHead(200);            response.end(              JSON.stringify({                result: true,                data: aesEncrypt(                  JSON.stringify({ userId: 123, address: "杭州" }), // 这个数据会被加密                  aesKey                ),              })            );          });          return;        }        break;      default:        break;    }    response.writeHead(404);    response.end();  })  .listen(3000);
      • 加密和解密办法

        function decrypt({ param1, param2 }) {  const decrypted = rsaKey.decrypt(param2, "utf8"); // 解密失去aesKey  aesKey = decrypted.split(",").map((item) => {    return +item;  });  return aesDecrypt(param1, aesKey);}/** * aes解密办法 * @param {string} encryptedHex 加密的字符串
   */  function aesDecrypt(encryptedHex, key) {    const encryptedBytes = aesjs.utils.hex.toBytes(encryptedHex); // 把十六进制转成二进制数据    const aesCtr = new aesjs.ModeOfOperation.ctr(key, new aesjs.Counter(5)); // 这边应用CTR-Counter加密模式,还有其余模式能够抉择,具体能够参考aes加密库      const decryptedBytes = aesCtr.decrypt(encryptedBytes); // 进行解密    const decryptedText = aesjs.utils.utf8.fromBytes(decryptedBytes); // 把二进制数据转成字符串      return decryptedText;  }    /**   * aes加密办法   * @param {string} text 待加密的字符串   * @param {array} key 加密key   */  function aesEncrypt(text, key) {    const textBytes = aesjs.utils.utf8.toBytes(text); // 把字符串转成二进制数据    const aesCtr = new aesjs.ModeOfOperation.ctr(key, new aesjs.Counter(5));      const encryptedBytes = aesCtr.encrypt(textBytes); // 加密    const encryptedHex = aesjs.utils.hex.fromBytes(encryptedBytes); // 把二进制数据转成十六进制      return encryptedHex;  }  ```
  • 残缺的示例代码

演示成果

总结

本文次要介绍了一些前端平安方面的常识和具体加密计划的实现。为了爱护客户的隐衷数据,不论是 HTTP 还是HTTPS,都倡议密文传输信息,让破解者减少一点攻打难度吧。当然数据加解密也会带来肯定性能上的耗费,这个须要各位开发者各自掂量了。

参考文献

看完这篇文章,我奶奶都懂了https的原理

中间人攻打

招贤纳士

政采云前端团队(ZooTeam),一个年老富裕激情和创造力的前端团队,隶属于政采云产品研发部,Base 在风景如画的杭州。团队现有 40 余个前端小伙伴,平均年龄 27 岁,近 3 成是全栈工程师,妥妥的青年风暴团。成员形成既有来自于阿里、网易的“老”兵,也有浙大、中科大、杭电等校的应届新人。团队在日常的业务对接之外,还在物料体系、工程平台、搭建平台、性能体验、云端利用、数据分析及可视化等方向进行技术摸索和实战,推动并落地了一系列的外部技术产品,继续摸索前端技术体系的新边界。

如果你想扭转始终被事折腾,心愿开始能折腾事;如果你想扭转始终被告诫须要多些想法,却无从破局;如果你想扭转你有能力去做成那个后果,却不须要你;如果你想扭转你想做成的事须要一个团队去撑持,但没你带人的地位;如果你想扭转既定的节奏,将会是“5 年工作工夫 3 年工作教训”;如果你想扭转原本悟性不错,但总是有那一层窗户纸的含糊… 如果你置信置信的力量,置信平凡人能成就不凡事,置信能遇到更好的本人。如果你心愿参加到随着业务腾飞的过程,亲手推动一个有着深刻的业务了解、欠缺的技术体系、技术发明价值、影响力外溢的前端团队的成长历程,我感觉咱们该聊聊。任何工夫,等着你写点什么,发给 ZooTeam@cai-inc.com