前言
在现今这个互联网发展高速发展的社会,互联网成为了很多人的得力助手,但是对于我们个人而言,也有诸多烦恼,有些网站或者APP都需要个人注册,才能存储对应的用户信息。这时候就会出现一个比较麻烦的问题,注册账户通常需要有用户名和密码,一般来说用户名不算涉及个人的隐私,但有时也需要个人手机号或者身份证号,这时候就要注意保护这个信息,当然密码是更重要的,现在这个社会,和自己关联的网站或者APP至少有上百个,例如比较重要的通讯或者支付类的密码,我们个人也不容易记得所有的密码,一般是相似或者相同的。
从上述这些话中,我们明白个人的账户和密码对自己很重要,一旦被别人获取,会有各种各样的麻烦和损失。那在网站或者APP中,我们在登录时,用户名和密码是明文传输吗,这样会不会被别人获取呢?答案是不是的,这种一般在设计之初,前端会在用户注册时,对用户的密码进行加密,连同账户名信息传输给后端,然后后端进行保存,将其存储在数据库中。下次用户登录时,就会把用户名和加密后的密码,传给后端,然后后端根据用户名等唯一关联性信息进行密码比对,如果对应的上,则登录成功,否则则登录失败,密码错误
。
在上面的讲述中,主要有两个比较关键的信息:
前端加密
:前端加密是指在前端(即客户端)对数据进行加密的过程。这通常用于保护用户输入的敏感信息(如用户名、密码、信用卡号等)在传输过程中的安全性。- 用户注册时,
是将加密后的密码传输给后端,这样后端其实也不知道用户的密码
,这样设计的目的是确保在密码传输和存储过程中,即使数据库被盗或者网络传输被监听,也无法直接获取到用户的原始密码。这也就解释了,通常忘记密码为啥不能直接找回密码,因为后端也不知道用户密码是啥。
本文也主要针对上述两部分进行讲解。
前端加密介绍
前端加密
主要是为了保护敏感数据在传输过程中的安全性
,防止数据被窃取或篡改
。通过在前端进行加密,可以增加数据的保密性和完整性
。
常见的前端加密场景包括:
用户密码的加密
:在用户注册或登录时,将密码进行加密后再传输给后端进行验证,增加密码的安全性。敏感数据的加密
:如个人身份证号码、银行卡号等敏感信息,在传输或存储之前进行加密,以防止被恶意获取。
常见的前端加密方法包括对称加密、非对称加密和哈希加密:
对称加密
:对称加密使用相同的密钥进行加密和解密。常见的对称加密算法包括AES(高级加密标准)和DES(数据加密标准)。在前端中,可以使用CryptoJS等库来实现对称加密。非对称加密
:非对称加密使用一对密钥,分别是公钥和私钥。公钥用于加密数据,私钥用于解密数据。常见的非对称加密算法包括RSA和ECC。在前端中,可以使用OpenSSL、Forge等库来实现非对称加密。哈希加密
:哈希加密是一种单向加密算法,它将输入的数据转换为固定长度的哈希值。常见的哈希算法包括MD5、SHA-1、SHA-256等。在前端中,可以使用JavaScript内置的Crypto API或者第三方库来实现哈希加密。
除了以上的加密方法,还有一些其他的加密技术,比如混合加密、数字签名等。在实际应用中,需要根据具体的需求和安全要求选择合适的加密方法。同时,需要注意加密算法的安全性和性能,以及密钥管理等问题。
对称加密
对称加密
是一种常见的前端加密方法,它使用相同的密钥来进行加密和解密
,因此密钥管理
至关重要。
常见的前端对称加密方法有AES(Advanced Encryption Standard)、DES(Data Encryption Standard)和3DES(Triple DES)。
- AES(Advanced Encryption Standard)是目前最常用的对称加密算法,它具有高安全性和快速的加解密速度。在前端使用时,需要生成一个随机的密钥,并使用安全的方式将密钥传输到后端进行解密。
- DES(Data Encryption Standard)是较早的对称加密算法,现在已经很少使用,因为它的安全性相对较低。
- 3DES(Triple DES)是对DES的改进版本,它将DES算法执行三次来提高安全性。
基本应用步骤:
- 选择合适的算法和密钥长度:
- 根据安全要求和性能考量,选择适当的对称加密算法和密钥长度。
- 密钥的生成和管理:
- 使用安全的随机数生成器生成密钥,确保密钥的安全性和难以被猜测。
- 数据加密:
- 在前端,使用选定的对称加密算法对需要保护的数据进行加密。例如,使用 AES 对用户密码、身份验证令牌等敏感信息进行加密处理。
- 数据解密:
- 当需要访问加密数据时,前端应用使用相同的密钥和加密算法对数据进行解密,以获取原始的明文数据。
- 安全传输:
- 在数据传输过程中,可以结合 HTTPS 协议来保证加密数据的安全传输,防止中间人攻击和窃听。
示例代码:
// 使用AES加密数据
import CryptoJS from 'crypto-js';
const secretKey = 'your_secret_key'; // 随机生成的密钥
const encryptData = (data) => {
const encrypted = CryptoJS.AES.encrypt(data, secretKey).toString();
return encrypted;
};
const data = 'your_data';
const encryptedData = encryptData(data);
console.log(encryptedData); // 输出加密后的数据
需要注意的是,前端加密不能完全保证数据的绝对安全,因为前端代码是可以被查看和修改的。因此,前端加密应该与后端安全措施相结合,例如使用HTTPS协议传输数据、在后端进行进一步的验证和加密等,以提供更全面的数据安全保护。
非对称加密
非对称加密
是一种使用公钥
和私钥
对的加密技术,其中公钥用于加密
,私钥用于解密
。它广泛应用于数据传输的安全性和验证身份。
常见的前端非对称加密方法包括RSA(Rivest-Shamir-Adleman)和ECC(Elliptic Curve Cryptography)。
RSA(Rivest-Shamir-Adleman)
: RSA是一种非对称加密算法,它使用一个公钥和一个私钥来进行加密和解密。在前端中,可以使用JavaScript中的Crypto API或者第三方库如Forge来实现RSA加密。ECC(Elliptic Curve Cryptography)
: ECC是另一种非对称加密算法,它比RSA算法更加高效。在前端中,也可以使用JavaScript中的Crypto API或者第三方库来实现ECC加密。
使用Web Crypto API
Web Crypto API
是浏览器内置的加密API,支持非对称加密
。它提供了生成密钥对、加密、解密等功能。
🍺生成密钥对
async function generateKeyPair() {
const keyPair = await window.crypto.subtle.generateKey({
name: "RSA-OAEP",
modulusLength: 2048,
publicExponent: new Uint8Array([1, 0, 1]),
hash: {name: "SHA-256"},
}, true, ["encrypt", "decrypt"]);
return keyPair;
}
🍺加密数据
async function encryptData(publicKey, data) {
const encodedData = new TextEncoder().encode(data);
const encryptedData = await window.crypto.subtle.encrypt({
name: "RSA-OAEP",
}, publicKey, encodedData);
return encryptedData;
}
🍺解密数据
async function decryptData(privateKey, encryptedData) {
const decryptedData = await window.crypto.subtle.decrypt({
name: "RSA-OAEP",
}, privateKey, encryptedData);
return new TextDecoder().decode(decryptedData);
}
使用jsencrypt实现RSA
jsencrypt
是一个 JavaScript 库,用于实现 RSA 加密和解密。
jsencrypt
是一个 JavaScript 库,用于实现 RSA 加密和解密。以下是如何在前端使用 jsencrypt
实现 RSA 加密和解密的步骤:
🍻1. 引入 jsencrypt
首先,需要在项目中引入 jsencrypt
库。可以通过 CDN 或 npm 安装。
使用 CDN
<script src="https://cdnjs.cloudflare.com/ajax/libs/jsencrypt/3.0.0/jsencrypt.min.js"></script>
使用 npm
npm install jsencrypt
🍻2. 生成 RSA 密钥对
你可以使用 jsencrypt
或其他工具生成 RSA 密钥对。这里我们使用 jsencrypt
来演示生成和使用密钥对。
// 引入 jsEncrypt
import { JSEncrypt } from 'jsencrypt';
// 创建 jsEncrypt 实例
const jsEncrypt = new JSEncrypt({default_key_size: 2048});
// 生成公钥和私钥
const publicKey = jsEncrypt.getPublicKey();
const privateKey = jsEncrypt.getPrivateKey();
console.log("Public Key:", publicKey);
console.log("Private Key:", privateKey);
🍻3. 使用公钥加密
用公钥加密数据时,可以通过 jsEncrypt
实例的 setPublicKey
方法设置公钥,然后使用 encrypt
方法加密数据。
// 设置公钥
jsEncrypt.setPublicKey(publicKey);
// 要加密的数据
const data = "Hello, RSA!";
// 加密数据
const encrypted = jsEncrypt.encrypt(data);
console.log("Encrypted Data:", encrypted);
🍻4. 使用私钥解密
用私钥解密数据时,可以通过 jsEncrypt
实例的 setPrivateKey
方法设置私钥,然后使用 decrypt
方法解密数据。
// 设置私钥
jsEncrypt.setPrivateKey(privateKey);
// 解密数据
const decrypted = jsEncrypt.decrypt(encrypted);
console.log("Decrypted Data:", decrypted);
方法封装
// 导入jsencrypt库
import * as JSEncrypt from 'jsencrypt';
// 生成RSA密钥对
const generateKeyPair = () => {
const encryptor = new JSEncrypt.JSEncrypt();
const privateKey = encryptor.getPrivateKey();
const publicKey = encryptor.getPublicKey();
return { privateKey, publicKey };
};
// 加密数据
const encryptData = (data, publicKey) => {
const encryptor = new JSEncrypt.JSEncrypt();
encryptor.setPublicKey(publicKey);
const encrypted = encryptor.encrypt(data);
return encrypted;
};
// 解密数据
const decryptData = (encryptedData, privateKey) => {
const decryptor = new JSEncrypt.JSEncrypt();
decryptor.setPrivateKey(privateKey);
const decrypted = decryptor.decrypt(encryptedData);
return decrypted;
};
// 生成RSA密钥对
const keyPair = generateKeyPair();
const publicKey = keyPair.publicKey;
const privateKey = keyPair.privateKey;
// 要加密的数据
const data = 'your_data';
// 使用公钥加密数据
const encryptedData = encryptData(data, publicKey);
console.log(encryptedData); // 输出加密后的数据
// 使用私钥解密数据
const decryptedData = decryptData(encryptedData, privateKey);
console.log(decryptedData); // 输出解密后的数据
注意事项
- 密钥长度:默认密钥长度为2048位,可以根据需要调整,但必须为2048位或以上以保证安全性。
- 加密数据长度:RSA加密适合小数据块,大数据块应先使用对称加密(如AES),然后用RSA加密对称密钥。
通过这些步骤,你可以使用 jsencrypt
在前端实现 RSA 加密和解密,确保数据传输的安全性。
哈希加密
哈希加密
是前端加密中的一种常见方法,它将任意长度的数据映射为固定长度的哈希值
,通常用于数据的完整性校验和密码存储。
哈希加密
是一种单向加密
方法,它将输入的数据通过哈希函数转换成固定长度的哈希值。同样的输入会产生同样的哈希值,而不同的输入则会产生不同的哈希值
。哈希函数是不可逆
的,即无法从哈希值还原出原始数据
。
在前端中,哈希加密常用于密码存储和数据完整性验证。例如,当用户注册时,可以将其密码进行哈希加密后存储在数据库中,而不是明文存储密码。当用户登录时,将其输入的密码进行哈希加密后与数据库中的哈希值进行比对,以验证密码的正确性。
以下是哈希加密在前端中的常见实现方法:
-
MD5(Message Digest Algorithm 5)
: MD5 是一种常用的哈希算法,它将任意长度的数据映射为 128 位的哈希值。-
在前端中,可以使用 JavaScript 中的 Crypto API 或者第三方库如 js-md5 来实现 MD5 哈希加密。
// 使用js-md5进行MD5哈希加密 var hashValue = md5('plaintext');
-
-
SHA(Secure Hash Algorithm)
: SHA 是另一种常用的哈希算法,它将任意长度的数据映射为固定长度的哈希值,通常有 SHA-1、SHA-2 和 SHA-3 等不同的版本。-
在前端中,可以使用 JavaScript 中的 Crypto API 或者第三方库如 crypto-js 来实现 SHA 哈希加密。
// 使用crypto-js进行SHA哈希加密 var hashValue = CryptoJS.SHA256('plaintext');
-
优缺点
🥃优点
- 不可逆性:哈希函数是单向的,无法从哈希值还原出原始数据,增加了数据的安全性。
- 快速计算:哈希函数的计算速度通常很快,适合处理大量数据。
- 固定长度:哈希加密算法将任意长度的数据映射为固定长度的哈希值,方便数据的存储和比较。
🥃缺点
- 碰撞风险:虽然哈希函数设计的目标是避免碰撞,但理论上存在两个不同的输入产生相同哈希值的可能性,这被称为哈希碰撞。
- 易受彩虹表攻击:彩虹表是一种预先计算出的哈希值和对应原始数据的映射表,攻击者可以使用彩虹表来快速破解哈希值。
- 不适合加密敏感数据:由于哈希加密算法是不可逆的,因此不适合用于加密敏感数据,如信用卡号、社保号等。
在实际应用中,为了增强安全性,通常会结合盐值(salt)和/或使用多次哈希(如bcrypt或scrypt算法)来进一步提高密码存储的安全性。
现在使用哈希算法加密已经很少了,使用的非对称加密算法比较多,所以文本只做简略介绍。
忘记密码,为啥后端不直接返回原密码
在第一章中,我们介绍了,忘记密码时,其实后端存储的密码也是我们注册或者修改密码后加密的数据,后端其实也不知道我们的密码是啥,这样设计的目的是确保在密码传输和存储过程中,即使数据库被盗或者网络传输被监听,也无法直接获取到用户的原始密码。这也就解释了,通常忘记密码为啥不能直接找回密码,因为后端也不知道用户密码是啥。
唉,以前用了很长时间的QQ号,后面一段时间不用了,手机号也不用了,后面使用时,已经忘记了密码,就无法修改密码,唉,只能忍痛不使用这个QQ号了。
后端不直接返回原始密码的原因主要是出于安全考虑。以下是几个关键点:
- 密码不可逆性:通常,用户的密码在存储时会经过哈希处理,哈希函数是单向的,即从密码生成哈希值容易,但从哈希值反推出原始密码非常困难。因此,后端通常无法直接获取原始密码。
- 保护用户隐私:即使后端能够访问原始密码,直接返回也会暴露用户隐私,违反了密码管理的基本原则。用户应该有权更改和管理自己的密码,而服务提供商不应知道或存储明文密码。
- 减少风险:如果后端服务器被攻击,直接存储的明文密码将使所有用户账户的安全性受到威胁。而通过重置密码流程,即使攻击者获取了重置链接或验证码,也仅能临时改变密码,而不是直接获取所有用户密码。
- 符合安全标准:遵循安全最佳实践,如OWASP(开放网络应用安全项目)的建议,密码重置流程不应涉及返回原始密码。
因此,通过重置密码而不是返回原始密码,可以确保用户账户的安全,并遵循了现代密码管理的安全原则。