敏感数据加密方案及实现

前言

现在是大数据时代,需要收集大量的个人信息用于统计。一方面它给我们带来了便利,另一方面一些个人信息数据在无意间被泄露,被非法分子用于推销和黑色产业。

2018 年 5 月 25 日,欧盟已经强制执行《通用数据保护条例》(General Data Protection Regulation,缩写作 GDPR)。该条例是欧盟法律中对所有欧盟个人关于数据保护和隐私的规范。这意味着个人数据必须使用假名化或匿名化进行存储,并且默认使用尽可能最高的隐私设置,以避免数据泄露。

[[348233]]

相信大家也都不想让自己在外面“裸奔”。所以,作为前端开发人员也应该尽量避免用户个人数据的明文传输,尽可能的降低信息泄露的风险。

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

数据泄露方式

1. 中间人攻击

中间人攻击是常见的攻击方式。详细过程可以参见这里:

https://zh.wikipedia.org/wiki/%E4%B8%AD%E9%97%B4%E4%BA%BA%E6%94%BB%E5%87%BB。大概的过程是中间人通过 DNS 欺骗等手段劫持了客户端与服务端的会话。

客户端、服务端之间的信息都会经过中间人,中间人可以获取和转发两者的信息。在 HTTP 下,前端数据加密还是避免不了数据泄露,因为中间人可以伪造密钥。为了避免中间人攻击,我们一般采用 HTTPS 的形式传输。

2. 谷歌插件

HTTPS 虽然可以防止数据在网络传输过程中被劫持,但是在发送 HTTPS 之前,数据还是可以从谷歌插件中泄露出去。

因为谷歌插件可以捕获 Network 中的所有请求,所以如果某些插件中有恶意的代码还是可以获取到用户信息的,下面为大家演示。

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

加密算法介绍

1. 对称加密

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

这就要求加密和解密方事先都必须知道加密的密钥。其优点是算法公开、计算量小、加密速度快、加密效率高;缺点是密钥泄露之后,数据就会被破解。一般不推荐单独使用。根据实现机制的不同,常见的算法主要有:

  • AES(https://zh.wikipedia.org/wiki/%E9%AB%98%E7%BA%A7%E5%8A%A0%E5%AF%86%E6%A0%87%E5%87%86)
  • ChaCha20 (https://zh.wikipedia.org/wiki/Salsa20#ChaCha20)、3DES (https://zh.wikipedia.org/wiki/3DES)等。

2. 非对称加密

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

他俩是配对生成的,就像钥匙和锁的关系。因为加密和解密使用的是两个不同的密钥,所以这种算法称为非对称加密算法。其优点是算法强度复杂、安全性高;缺点是加解密速度没有对称加密算法快。常见的算法主要有:

  • RSA (https://zh.wikipedia.org/wiki/RSA%E5%8A%A0%E5%AF%86%E6%BC%94%E7%AE%97%E6%B3%95)
  • Elgamal (https://zh.wikipedia.org/wiki/ElGamal%E5%8A%A0%E5%AF%86%E7%AE%97%E6%B3%95)等。

3. 散列算法

散列算法又称散列函数、哈希函数,是把消息或数据压缩成摘要,使得数据量变小,将数据的格式固定成特定长度的值。一般用于校验数据的完整性,平时我们下载文件就可以校验 MD5 来判断下载的数据是否完整。常见的算法主要有:

  • MD4 (https://zh.wikipedia.org/wiki/MD4)
  • MD5 (https://zh.wikipedia.org/wiki/MD5)
  • SHA (https://zh.wikipedia.org/wiki/SHA%E5%AE%B6%E6%97%8F) 等。

实现方案

方案一:如果用对称加密,那么服务端和客户端都必须知道密钥才行。那服务端势必要把密钥发送给客户端,这个过程中是不安全的,所以单单用对称加密行不通。

方案二:如果用非对称加密,客户端的数据通过公钥加密,服务端通过私钥解密,客户端发送数据实现加密没问题。客户端接受数据,需要服务端用公钥加密,然后客户端用私钥解密。所以这个方案需要两套公钥和私钥,需要在客户端和服务端各自生成自己的密钥。

方案三:如果把对称加密和非对称加密相结合。客户端需要生成一个对称加密的密钥 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/jsencrypt
  • 具体代码实现登录接口

(1) 客户端需要随机生成一个 aesKey,在页面加载完的时候需要从服务端请求 publicKey

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

2. aes 加密和解密方法

  1. /** 
  2.  * aes加密方法 
  3.  * @param {string} text 待加密的字符串 
  4.  * @param {array} key 加密key 
  5.  */ 
  6. function aesEncrypt(text, key) { 
  7.   const textBytes = aesjs.utils.utf8.toBytes(text); // 把字符串转换成二进制数据 
  8.  
  9.   // 这边使用CTR-Counter加密模式,还有其他模式可以选择,具体可以参考aes加密库 
  10.   const aesCtr = new aesjs.ModeOfOperation.ctr(key, new aesjs.Counter(5)); 
  11.  
  12.   const encryptedBytes = aesCtr.encrypt(textBytes); // 进行加密 
  13.   const encryptedHex = aesjs.utils.hex.fromBytes(encryptedBytes); // 把二进制数据转成十六进制 
  14.  
  15.   return encryptedHex; 
  16.  
  17. /** 
  18.  * aes解密方法 
  19.  * @param {string} encryptedHex 加密的字符串 
  20.  * @param {array} key 加密key 
  21.  */ 
  22. function aesDecrypt(encryptedHex, key) { 
  23.   const encryptedBytes = aesjs.utils.hex.toBytes(encryptedHex); // 把十六进制数据转成二进制 
  24.   const aesCtr = new aesjs.ModeOfOperation.ctr(key, new aesjs.Counter(5)); 
  25.  
  26.   const decryptedBytes = aesCtr.decrypt(encryptedBytes); // 进行解密 
  27.   const decryptedText = aesjs.utils.utf8.fromBytes(decryptedBytes); // 把二进制数据转成utf-8字符串 
  28.  
  29.   return decryptedText; 

3. 请求登录

  1. /** 
  2.  * 登陆接口 
  3.  */ 
  4. function submitFn() { 
  5.   const userName = document.querySelector("#userName").value; 
  6.   const password = document.querySelector("#password").value; 
  7.   const data = { 
  8.     userName, 
  9.     password, 
  10.   }; 
  11.  
  12.   const text = JSON.stringify(data); 
  13.   const sendData = aesEncrypt(text, aesKey); // 把要发送的数据转成字符串进行加密 
  14.   console.log("发送数据", text); 
  15.  
  16.   const encrypt = new JSEncrypt(); 
  17.   encrypt.setPublicKey(publicKey); 
  18.   const encryptencrypted = encrypt.encrypt(aesKey.toString()); // 把aesKey进行非对称加密 
  19.  
  20.   const url = "http://localhost:3000/login"
  21.   const params = { id: 0, data: { param1: sendData, param2: encrypted } }; 
  22.  
  23.   axios({ 
  24.     method: "POST", 
  25.     headers: { "content-type": "application/x-www-form-urlencoded" }, 
  26.     url: url, 
  27.     data: JSON.stringify(params), 
  28.   }) 
  29.     .then(function (result) { 
  30.       const reciveData = aesDecrypt(result.data.data, aesKey); // 用aesKey进行解密 
  31.       console.log("接收数据", reciveData); 
  32.     }) 
  33.     .catch(function (error) { 
  34.       console.log("error", error); 
  35.     }); 

服务端(Node):

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

(1) 引用加密库

  1. const http = require("http"); 
  2. const aesjs = require("aes-js"); 
  3. const NodeRSA = require("node-rsa"); 
  4. const rsaKey = new NodeRSA({ b: 1024 }); // key的size为1024位 
  5. let aesKey = null; // 用于保存客户端的aesKey 
  6. let privateKey = ""; // 用于保存服务端的公钥 
  7.  
  8. rsaKey.setOptions({ encryptionScheme: "pkcs1" }); // 设置加密模式 

(2) 实现 login 接口

  1. http 
  2.   .createServer((request, response) => { 
  3.     response.setHeader("Access-Control-Allow-Origin", "*"); 
  4. response.setHeader("Access-Control-Allow-Headers", "Content-Type"); 
  5.     response.setHeader("Content-Type", "application/json"); 
  6. switch (request.method) { 
  7.       case "GET": 
  8.         if (request.url === "/getPub") { 
  9.           const publicKey = rsaKey.exportKey("public"); 
  10.           privateKey = rsaKey.exportKey("private"); 
  11.           response.writeHead(200); 
  12.           response.end(JSON.stringify({ result: true, data: publicKey })); // 把公钥发送给客户端 
  13.           return; 
  14.         } 
  15.         break; 
  16.       case "POST": 
  17.         if (request.url === "/login") { 
  18.           let str = ""
  19.           request.on("data", function (chunk) { 
  20.             str += chunk; 
  21.           }); 
  22.           request.on("end", function () { 
  23.             const params = JSON.parse(str); 
  24.             const reciveData = decrypt(params.data); 
  25.             console.log("reciveData", reciveData); 
  26.             // 一系列处理之后 
  27.  
  28.             response.writeHead(200); 
  29.             response.end( 
  30.               JSON.stringify({ 
  31.                 result: true, 
  32.                 data: aesEncrypt( 
  33.                   JSON.stringify({ userId: 123, address: "杭州" }), // 这个数据会被加密 
  34.                   aesKey 
  35.                 ), 
  36.               }) 
  37.             ); 
  38.           }); 
  39.           return; 
  40.         } 
  41.         break; 
  42.       default: 
  43.         break; 
  44.     } 
  45.     response.writeHead(404); 
  46.     response.end(); 
  47.   }) 
  48.   .listen(3000); 

3. 加密和解密方法

  1. function decrypt({ param1, param2 }) { 
  2.   const decrypted = rsaKey.decrypt(param2, "utf8"); // 解密得到aesKey 
  3.   aesKey = decrypted.split(",").map((item) => { 
  4. return +item; 
  5.   }); 
  6.  
  7.   return aesDecrypt(param1, aesKey); 
  8.  
  9. /** 
  10.  * aes解密方法 
  11.  * @param {string} encryptedHex 加密的字符串 
  12.  * @param {array} key 加密key 
  13.  */ 
  14. function aesDecrypt(encryptedHex, key) { 
  15.   const encryptedBytes = aesjs.utils.hex.toBytes(encryptedHex); // 把十六进制转成二进制数据 
  16.   const aesCtr = new aesjs.ModeOfOperation.ctr(key, new aesjs.Counter(5)); // 这边使用CTR-Counter加密模式,还有其他模式可以选择,具体可以参考aes加密库 
  17.  
  18.   const decryptedBytes = aesCtr.decrypt(encryptedBytes); // 进行解密 
  19.   const decryptedText = aesjs.utils.utf8.fromBytes(decryptedBytes); // 把二进制数据转成字符串 
  20.  
  21.   return decryptedText; 
  22.  
  23. /** 
  24.  * aes加密方法 
  25.  * @param {string} text 待加密的字符串 
  26.  * @param {array} key 加密key 
  27.  */ 
  28. function aesEncrypt(text, key) { 
  29.   const textBytes = aesjs.utils.utf8.toBytes(text); // 把字符串转成二进制数据 
  30.   const aesCtr = new aesjs.ModeOfOperation.ctr(key, new aesjs.Counter(5)); 
  31.  
  32.   const encryptedBytes = aesCtr.encrypt(textBytes); // 加密 
  33.   const encryptedHex = aesjs.utils.hex.fromBytes(encryptedBytes); // 把二进制数据转成十六进制 
  34.  
  35.   return encryptedHex; 

完整的示例代码:https://github.com/Pulset/FrontDataEncrypt

演示效果

总结

本文主要介绍了一些前端安全方面的知识和具体加密方案的实现。为了保护客户的隐私数据,不管是 HTTP 还是 HTTPS,都建议密文传输信息,让破解者增加一点攻击难度吧。当然数据加解密也会带来一定性能上的消耗,这个需要各位开发者各自衡量了。

 

文章来源网络,作者:运维,如若转载,请注明出处:https://shuyeidc.com/wp/143587.html<

(0)
运维的头像运维
上一篇2025-03-09 18:41
下一篇 2025-03-09 18:43

相关推荐

  • 个人主题怎么制作?

    制作个人主题是一个将个人风格、兴趣或专业领域转化为视觉化或结构化内容的过程,无论是用于个人博客、作品集、社交媒体账号还是品牌形象,核心都是围绕“个人特色”展开,以下从定位、内容规划、视觉设计、技术实现四个维度,详细拆解制作个人主题的完整流程,明确主题定位:找到个人特色的核心主题定位是所有工作的起点,需要先回答……

    2025-11-20
    0
  • 社群营销管理关键是什么?

    社群营销的核心在于通过建立有温度、有价值、有归属感的社群,实现用户留存、转化和品牌传播,其管理需贯穿“目标定位-内容运营-用户互动-数据驱动-风险控制”全流程,以下从五个维度展开详细说明:明确社群定位与目标社群管理的首要任务是精准定位,需明确社群的核心价值(如行业交流、产品使用指导、兴趣分享等)、目标用户画像……

    2025-11-20
    0
  • 香港公司网站备案需要什么材料?

    香港公司进行网站备案是一个涉及多部门协调、流程相对严谨的过程,尤其需兼顾中国内地与香港两地的监管要求,由于香港公司注册地与中国内地不同,其网站若主要服务内地用户或使用内地服务器,需根据服务器位置、网站内容性质等,选择对应的备案路径(如工信部ICP备案或公安备案),以下从备案主体资格、流程步骤、材料准备、注意事项……

    2025-11-20
    0
  • 如何企业上云推广

    企业上云已成为数字化转型的核心战略,但推广过程中需结合行业特性、企业痛点与市场需求,构建系统性、多维度的推广体系,以下从市场定位、策略设计、执行落地及效果优化四个维度,详细拆解企业上云推广的实践路径,精准定位:明确目标企业与核心价值企业上云并非“一刀切”的方案,需先锁定目标客户群体,提炼差异化价值主张,客户分层……

    2025-11-20
    0
  • PS设计搜索框的实用技巧有哪些?

    在PS中设计一个美观且功能性的搜索框需要结合创意构思、视觉设计和用户体验考量,以下从设计思路、制作步骤、细节优化及交互预览等方面详细说明,帮助打造符合需求的搜索框,设计前的规划明确使用场景:根据网站或APP的整体风格确定搜索框的调性,例如极简风适合细线条和纯色,科技感适合渐变和发光效果,电商类则可能需要突出搜索……

    2025-11-20
    0

发表回复

您的邮箱地址不会被公开。必填项已用 * 标注