當(dāng)你知道如何在應(yīng)用中針對不同的使用場景使用不同的加密算法蒸眠,你的應(yīng)用才是安全的杆融。因此,這篇文章將會介紹平常工作中比較常用的幾種加密方式蒋腮,并給出對應(yīng)的Node.js代碼藕各。
保護密碼
用戶在注冊應(yīng)用的時候難免會使用一些過于簡單的密碼,同時也傾向于在不同的站點使用相同的密碼作彤。最可怕的是用來存儲用戶密碼的數(shù)據(jù)庫可能被黑客侵入乌逐。因此,對于用戶密碼的保護是最基礎(chǔ)的防護措施绢慢。(ps: 大家可以使用這個網(wǎng)站來檢查自己的密碼是否被泄露了洛波。)
對于密碼的加密,通常使用的是哈希(hash)算法缚窿。比較常見的哈希算法有Argon2焰扳、PBKDF2够话、scrypt和bcrypt等女嘲。關(guān)于這些加密算法的介紹诞帐,大家可以參考Password Storage Cheat Sheet這篇文章。
node.js中提供了crypto模塊愕鼓,該模塊主要用于實現(xiàn)基礎(chǔ)的加解密算法慧起。接下來就來介紹一下如何使用該模塊來實現(xiàn)對于密碼的保護:
// 這里是最基礎(chǔ)的md5算法
const crypto = require('crypto');
const hash = crypto.createHash('md5'); // 最基本的md5算法
hash.update('password1'); // update用來更新數(shù)據(jù)
hash.digest('hex'); // 以string格式輸出密文
hash.digest('sha256');
對于md5算法,這里推薦使用https://hashtoolkit.com/這個網(wǎng)站進行調(diào)試。普通的md5算法對于程序員來說就是明文的磺送,因此我們一般還會對它加鹽(salt the hash):
const crypto = require('crypto');
const password = 'ddd';
const salt = crypto.randomBytes(256).toString('hex');
// 這里使用pbkdf2算法
const hashedPwd = crypto.pbkdf2Sync(password, salt, 100000, 512, 'sha512');
console.log(hashedPwd.toString('hex'));
保護存儲數(shù)據(jù)
對稱加密算法(Symmetric Encryption)是一種使用同一密鑰進行加密和解密文本的算法估灿,這也意味著通信雙方要使用同一密鑰進行加解密缤剧。對稱加密算法對于大型數(shù)據(jù)的加密來說速度很快,因此它也主要用于存儲數(shù)據(jù)的加密汗销。比較常用的對稱加密算法有AES抵窒、Blowfish、DES和RC4等钦奋。
crypto模塊中主要利用以下方法來實現(xiàn)對稱加密:
createCipheriv: 提供對稱加密, 該方法接收三個參數(shù)疙赠,第一個是加密算法、第二個是密鑰厌衔,而第三個是初始向量(initialization vector)
update 和 final: 先更新數(shù)據(jù)捍岳,然后獲得密文
const crypto = require('crypto');
const algorithm = 'aes-256-cbc';
const password = 'Hello world';
const salt = crypto.randomBytes(32);
cnst key = crypto.scryptSync(password, salt, 32);
const iv = crypto.randomBytes(16);
const cipher = crypto.crateCipheriv(algorithm, key, iv);
let ssn = '111-000-2342';
let encrypted = cipher.update(ssn, 'utf8', 'hex');
// 獲得加密密文
encrypted += cipher.final('hex');
// 解密算法是反向操作
const decipher = crypto.createDecipheriv(algorithm, key, iv);
let decrypted = decipher.final('utf8');
console.log(decrypted)
同時用于對稱加密的密鑰也需要有合適的密碼管理策略進行處理,否則一旦密鑰被破解页徐,數(shù)據(jù)的安全性就無從保障变勇。通常我們會使用一個密鑰管理系統(tǒng)(KMS)進行管理密鑰。所有的加密密鑰都由該系統(tǒng)進行統(tǒng)一分配和管理搀绣,而用于數(shù)據(jù)加密的密鑰由一個主密鑰(master
key)進行加密链患。比較流行的KMS有https://cloud.google.com/kms/ 和 https://aws.amazon.com/kms/,當(dāng)然你可以選擇自己實現(xiàn)纲仍。
保護通信信道
存儲數(shù)據(jù)的保護使用的是對稱算法芯肤,與之相對應(yīng)的非對稱算法則主要用于保護通信信道压鉴。
const crypto = require('crypto');
// 加密方法
exports.encrypt = (data, key) => {
// 公鑰加密
return crypto.publicEncrypt(key, Buffer.from(data));
};
// 解密方法
exports.decrypt = (encrypted, key) => {
// 私鑰解密
return crypto.privateDecrypt(key, encrypted);
};
在數(shù)據(jù)傳輸過程中油吭,我們還會使用Diffie-Hellman算法進行交換密鑰:
const crypto = require('crypto');
const sally = crypto.createDiffieHellman(2048);
const sallKeys = sally.generateKeys();
const bob = crypto.createDiffieHellman(sally.getPrime(), sally.getGenerator());
const bobKey = bob.generateKeys();
const sallySecret = sally.computeSecret(bobKey);
const bobSecret = bob.computeSecret(sallyKeys);
console.log(sallySecret.toString('hex'));
console.log(bobSecret.toString('hex'));
接下來介紹一下HMAC算法的使用,這個算法主要用于身份認證和生成消息摘要:
const hmac = crypto.createHmac('sha256', 'a secret');
hmac.update('some data');
console.log(hmac.digest('hex'));
雙因子認證
雙因子認證(Two-factor authentication歌豺,也叫2FA)是一種組合使用兩種不同的驗證機制來確認用戶身份的機制心包。主要是通過手機等設(shè)備來生成token,具體到代碼實現(xiàn)層面痕惋,可以在服務(wù)端和客戶端之間生成一個臨時的代碼序列來校驗娃殖。
這里推薦使用speakeasy這個npm庫來實現(xiàn)2FA:
const express = require('express');
const app = express();
const speakeasy = require('speakeasy');
const qrcode = require('qrcode');
const bodyParser = require('body-parser');
app.use(bodyParser.urlencoded( { extended: true } ));
var router = express.Router();
var user = {
two_factor_temp_secret: null,
two_factor_secret: null,
two_factor_enabled: false
};
router.get('/2fa', function(req, res){
// 生成私鑰
var secret = speakeasy.generateSecret();
user.two_factor_temp_secret = secret.base32;
qrcode.toDataURL(secret.otpauth_url, function(err, data_url){
res.send('<img src="' + data_url + '">');
});
});
// 認證頁面
router.get('/authenticate', function(req, res){
res.send('<form action="/app/verify" method="post">Enter Token: <input type="text" name="token"><br><input type="submit" value="submit">');
});
//校驗用戶輸入
router.post('/verify', function(req, res){
var userToken = req.body.token;
var base32secret = user.two_factor_temp_secret;
var verified = speakeasy.totp.verify({
secret: base32secret,
encoding: 'base32',
token: userToken
});
if(verified){
user.two_factor_secret = user.two_factor_temp_secret;
user.two_factor_enabled = true;
console.log('Successfully verified');
res.send('<p>Your token has been verified!</p>');
} else {
console.log('verification failed');
res.send('<p>verification failed</p>');
}
});
app.use('/app', router);
app.listen(3000);
console.log('App is running on port 3000');
總結(jié)
這篇文章主要介紹了如何在Node.js中使用crypto模塊來保護密碼堕虹、存儲數(shù)據(jù)和通信信道。最后還介紹了如何使用speakeasy模塊來實現(xiàn)雙因子驗證機制逼裆,希望這篇文章對大家有所幫助螟炫。
參考資料
Zero to Hashing in Under 10 Minutes: Argon2 in Nodejs – Hunter2: AppSec Training
Symmetric Encryption in JavaScript - Ekene Izukanne - Medium