前端常用加密方式及使用方法示例代碼
一. 什么是前端加密?(先糾正一個(gè)常見誤區(qū))
前端加密,指的是在瀏覽器(js環(huán)境)中,對數(shù)據(jù)進(jìn)行加密/簽名/混淆/校驗(yàn)等操作,再發(fā)送給后端
重要認(rèn)知:
前端加密 ≠ 絕對安全
前端代碼是可被查看,可被調(diào)試,可被篡改的.
所以前端加密的核心目的不是[防止高手],而是:
- 防止明文傳輸
- 防止低成本抓包,腳本刷接口
- 提高攻擊門檻
- 與后端做配合校驗(yàn)
二 . 前端常見的加密[分類]
1.哈希(不可逆) :
(哈希也叫散列,是一種將任意長度的輸入如數(shù)據(jù),文件,消息)通過哈希函數(shù)轉(zhuǎn)換成固定長度輸出的過程,這個(gè)輸出通常成為哈希值,散列值或
摘要
用途:
1. 密碼處理
2. 簽名校驗(yàn)
3. 數(shù)據(jù)完整性校驗(yàn)
常見算法:
1. MD5(已不安全) : 可人為制造碰撞(指攻擊者找到兩個(gè)不同的輸入,經(jīng)過哈希計(jì)算后,卻產(chǎn)生相同的哈希值的過程)
2. SHA-1 (不推薦,逐漸被淘汰,存在碰撞攻擊)
3. SHA-256 和 SHA-512(推薦,屬于SHA-2),目前廣泛應(yīng)用
示例:
1.使用api : window.crypto.subte( ) 密碼學(xué)操作(需要HTTPS)
2. 也可以使用CryptoJS js 的加密庫, 支持多種加密算法(AES,SHA,MD5等)
// 1. 準(zhǔn)備數(shù)據(jù)
const text = "123456";
const encoder = new TextEncoder();
const data = encoder.encode(text);
// 2. 計(jì)算哈希
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
// 3. 轉(zhuǎn)為十六進(jìn)制
const hashArray = Array.from(new Uint8Array(hashBuffer));
const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
console.log("SHA-256:", hashHex);
// 輸出: 8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c922.對稱加密(前后端共用密鑰)
特點(diǎn):
- 加密解密使用同一個(gè)key
- 性能好,適合大量數(shù)據(jù)
常見算法:
- AES(主流): 是目前全球最常用的對稱加密算法
- DES(已過時(shí)): 被破解
示例:
// 最簡單的AES加密解密
async function aesSimple() {
// 1. 生成密鑰
const key = await crypto.subtle.generateKey(
{name: "AES-GCM", length: 256}, true, ["encrypt", "decrypt"]
);
// 2. 加密
const iv = crypto.getRandomValues(new Uint8Array(12));
const encrypted = await crypto.subtle.encrypt(
{name: "AES-GCM", iv}, key, new TextEncoder().encode("你好世界")
);
// 3. 解密
const decrypted = await crypto.subtle.decrypt(
{name: "AES-GCM", iv}, key, encrypted
);
console.log("結(jié)果:", new TextDecoder().decode(decrypted)); // 你好世界
}
aesSimple();3.非對稱加密(公鑰/私鑰): 先用它安全的交換對稱密鑰,然后用對稱加密處理數(shù)據(jù)!
特點(diǎn):
- 前端: 公鑰加密
- 后端: 私鑰解密
- 私鑰不暴露
適合:
- 密碼
- 關(guān)鍵字段
- 初始密鑰交換
缺點(diǎn):
- 慢
- 不適合大量數(shù)據(jù)
常見算法 :
- RSA(最常見): 原理來自于大數(shù)的質(zhì)因數(shù)分解
async function generateRSAKeys () { // 生成 RSA 密鑰對 const keyPair = await crypto.subtle.generateKey( { name: "RSA-OAEP", // RSA 算法 modulusLength: 2048, // 2048 位 publicExponent: new Uint8Array([0x01, 0x00, 0x01]), // 65537 hash: "SHA-256" // 使用的哈希 }, true, // 可導(dǎo)出 ["encrypt", "decrypt"] // 用途 ); return { publicKey: keyPair.publicKey, privateKey: keyPair.privateKey }; } async function rsaEncryptDecrypt () { console.log("=== RSA 加密解密示例 ==="); // 1. 生成密鑰對 const { publicKey, privateKey } = await generateRSAKeys(); console.log("密鑰對生成完成"); // 2. 準(zhǔn)備要加密的數(shù)據(jù) const message = "這是一條秘密消息"; const data = new TextEncoder().encode(message); // 3. 用公鑰加密 const encrypted = await crypto.subtle.encrypt( { name: "RSA-OAEP" }, publicKey, data ); console.log("加密完成,密文長度:", encrypted.byteLength, "字節(jié)"); // 4. 用私鑰解密 const decrypted = await crypto.subtle.decrypt( { name: "RSA-OAEP" }, privateKey, encrypted ); const decryptedMessage = new TextDecoder().decode(decrypted); console.log("解密結(jié)果:", decryptedMessage); return { publicKey, privateKey, encrypted }; } // 運(yùn)行示例 rsaEncryptDecrypt().catch(console.error);
ECC(更安全,少見) 加密原理來自于橢圓雙曲線
// ECC 密鑰交換完整流程演示
class ECCFlow {
static async demonstrateCompleteFlow() {
console.log("ECC 密鑰交換完整流程演示");
console.log("==========================");
// 第1步:選擇橢圓曲線
console.log("\n第1步:選擇橢圓曲線");
const curve = "P-256";
console.log(" 選擇的曲線: " + curve);
console.log(" 安全強(qiáng)度: 128位");
console.log(" 公鑰長度: 65字節(jié)");
console.log(" 私鑰長度: 32字節(jié)");
// 第2步:雙方生成密鑰對
console.log("\n第2步:雙方生成密鑰對");
// Alice 生成密鑰對
const aliceKeyPair = await crypto.subtle.generateKey(
{ name: "ECDH", namedCurve: curve },
true,
["deriveKey"]
);
console.log(" Alice 生成密鑰對:");
console.log(" - 私鑰: 保密存儲");
console.log(" - 公鑰: 準(zhǔn)備發(fā)送給 Bob");
// Bob 生成密鑰對
const bobKeyPair = await crypto.subtle.generateKey(
{ name: "ECDH", namedCurve: curve },
true,
["deriveKey"]
);
console.log(" Bob 生成密鑰對:");
console.log(" - 私鑰: 保密存儲");
console.log(" - 公鑰: 準(zhǔn)備發(fā)送給 Alice");
// 第3步:交換公鑰
console.log("\n第3步:交換公鑰(通過網(wǎng)絡(luò))");
const bobPublicKey = bobKeyPair.publicKey;
console.log(" Alice 收到 Bob 的公鑰");
const alicePublicKey = aliceKeyPair.publicKey;
console.log(" Bob 收到 Alice 的公鑰");
// 第4步:計(jì)算共享密鑰
console.log("\n第4步:各自計(jì)算共享密鑰");
console.log(" 數(shù)學(xué)原理:");
console.log(" Alice私鑰 × Bob公鑰 = Bob私鑰 × Alice公鑰");
// Alice 計(jì)算共享密鑰
const aliceSharedKey = await crypto.subtle.deriveKey(
{
name: "ECDH",
public: bobPublicKey
},
aliceKeyPair.privateKey,
{ name: "AES-GCM", length: 256 },
true,
["encrypt", "decrypt"]
);
console.log(" Alice 計(jì)算共享密鑰完成");
// Bob 計(jì)算共享密鑰
const bobSharedKey = await crypto.subtle.deriveKey(
{
name: "ECDH",
public: alicePublicKey
},
bobKeyPair.privateKey,
{ name: "AES-GCM", length: 256 },
true,
["encrypt", "decrypt"]
);
console.log(" Bob 計(jì)算共享密鑰完成");
// 第5步:驗(yàn)證密鑰相同
console.log("\n第5步:驗(yàn)證雙方密鑰相同");
const aliceKeyBytes = await crypto.subtle.exportKey("raw", aliceSharedKey);
const bobKeyBytes = await crypto.subtle.exportKey("raw", bobSharedKey);
const aliceHex = Array.from(new Uint8Array(aliceKeyBytes))
.map(b => b.toString(16).padStart(2, '0'))
.join('');
const bobHex = Array.from(new Uint8Array(bobKeyBytes))
.map(b => b.toString(16).padStart(2, '0'))
.join('');
console.log(" 驗(yàn)證結(jié)果:");
console.log(" Alice 密鑰: " + aliceHex.substring(0, 32) + "...");
console.log(" Bob 密鑰: " + bobHex.substring(0, 32) + "...");
console.log(" 是否相同: " + (aliceHex === bobHex ? "是" : "否"));
// 第6步:使用共享密鑰加密通信
console.log("\n第6步:使用共享密鑰加密通信");
const message = "會議時(shí)間改為下午2點(diǎn)";
console.log(" Alice 要發(fā)送: \"" + message + "\"");
// 加密
const iv = crypto.getRandomValues(new Uint8Array(12));
const encrypted = await crypto.subtle.encrypt(
{ name: "AES-GCM", iv },
aliceSharedKey,
new TextEncoder().encode(message)
);
console.log(" 加密完成:");
console.log(" - 密文長度: " + encrypted.byteLength + " 字節(jié)");
console.log(" - IV: " + Array.from(iv).slice(0, 3).join(',') + "...");
// 第7步:Bob 解密消息
console.log("\n第7步:Bob 解密消息");
const decrypted = await crypto.subtle.decrypt(
{ name: "AES-GCM", iv },
bobSharedKey,
encrypted
);
const decryptedMessage = new TextDecoder().decode(decrypted);
console.log(" Bob 解密得到: \"" + decryptedMessage + "\"");
console.log(" 是否正確: " + (message === decryptedMessage ? "是" : "否"));
// 安全性分析
console.log("\n安全性分析:");
console.log(" 中間人無法計(jì)算共享密鑰");
console.log(" 即使竊聽到公鑰交換也沒用");
console.log(" 每次會話可生成新的密鑰對");
return {
aliceKeyPair,
bobKeyPair,
sharedKey: aliceSharedKey,
encryptedMessage: encrypted
};
}
}
// 運(yùn)行完整流程
ECCFlow.demonstrateCompleteFlow().catch(console.error);4. 非對稱加密和對稱加密對比
特性 | 對稱加密 | 非對稱加密 |
|---|---|---|
密鑰數(shù)量? | 單個(gè)密鑰,加解密相同。 | 一對密鑰,公鑰加密,私鑰解密。 |
速度? | 非常快,適合加密大量數(shù)據(jù)。 | 非常慢(比對稱加密慢100-1000倍),不適合大數(shù)據(jù)。 |
安全性基礎(chǔ)? | 密鑰的保密性。密鑰必須安全共享。 | 密鑰對的數(shù)學(xué)關(guān)系。私鑰保密,公鑰可公開。 |
主要功能? | 保證數(shù)據(jù)的機(jī)密性。 | 保證機(jī)密性、身份認(rèn)證和不可否認(rèn)性。 |
密鑰分發(fā)? | 核心難題。如何安全地將密鑰傳遞給對方? | 天然解決。公鑰可以公開分發(fā),無需保密。 |
常見算法? | AES(高級加密標(biāo)準(zhǔn))、DES、3DES、ChaCha20 | RSA、ECC(橢圓曲線加密)、ElGamal |
典型應(yīng)用? | 文件加密、數(shù)據(jù)庫加密、SSL/TLS會話加密、Wi-Fi(WPA2) | SSL/TLS密鑰交換、數(shù)字簽名、電子郵件加密(PGP)、區(qū)塊鏈 |
5. 簽名≠加密
簽名的本質(zhì)是:
證明數(shù)據(jù)是"你"發(fā)的,且未被篡改
常見做法:
sign = hash(params + secret + timestamp)
//前端sign = sha256(a=1&b=2&secret=xxx&ts=123
后端:
- 用同樣規(guī)則計(jì)算
- 對比sign是否一致
三. 前端加密的底線原則
- 前端加密永遠(yuǎn)不是安全的終點(diǎn)
- 所有安全校驗(yàn),必須在后端完成
- 前端加密的價(jià)值是: 降低成本攻擊,而不是消滅攻擊
- 真正的安全 = https + 前端處理 + 后端校驗(yàn) + 風(fēng)控策略
總結(jié)
到此這篇關(guān)于前端常用加密方式及使用方法的文章就介紹到這了,更多相關(guān)前端常用加密方式內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Javascript查看大圖功能代碼實(shí)現(xiàn)
這篇文章主要介紹了Javascript查看大圖功能代碼實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-05-05
如何判別JS中的數(shù)據(jù)類型?一篇文章教你徹底弄懂typeof和instanceof
typeof和instanceof都是JavaScript中用于類型檢查的操作符,但它們的工作方式和應(yīng)用場景有顯著不同,這篇文章主要介紹了如何判別JS中的數(shù)據(jù)類型的相關(guān)資料,通過這篇文章可以徹底弄懂typeof和instanceof,需要的朋友可以參考下2025-08-08
Js圖片點(diǎn)擊切換輪播實(shí)現(xiàn)代碼
這篇文章主要介紹了Js圖片點(diǎn)擊切換輪播實(shí)現(xiàn)代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-07-07
javascript實(shí)現(xiàn)鼠標(biāo)選取拖動(dòng)或Ctrl選取拖動(dòng)
javascript實(shí)現(xiàn)鼠標(biāo)選取拖動(dòng)或Ctrl選取拖動(dòng)...2007-06-06
原生JS實(shí)現(xiàn)圖片無縫滾動(dòng)方法(附帶封裝的運(yùn)動(dòng)框架)
下面小編就為大家?guī)硪黄鶭S實(shí)現(xiàn)圖片無縫滾動(dòng)方法(附帶封裝的運(yùn)動(dòng)框架)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-10-10
layui數(shù)據(jù)表格重載實(shí)現(xiàn)往后臺傳參
今天小編就為大家分享一篇layui數(shù)據(jù)表格重載實(shí)現(xiàn)往后臺傳參,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-11-11
JavaScript異步拆解之Event-Loop+async/await
JS異步本質(zhì)是單線程下的生存策略,主線程串行執(zhí)行,耗時(shí)操作交由宿主后臺處理,通過事件循環(huán)(微/宏任務(wù)調(diào)度)保障交互不卡頓,這篇文章主要介紹了JavaScript異步拆解之Event-Loop+async/await的相關(guān)資料,需要的朋友可以參考下2026-03-03
JavaScript中數(shù)組常用的十種方法總結(jié)
JavaScript提供了許多強(qiáng)大的數(shù)組方法,讓你可以輕松地操作和修改數(shù)組,下面這篇文章主要介紹了JavaScript中數(shù)組常用的十種方法,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2025-09-09

