Node.js应用中常用安全保护机制和加密算法实现

当你知道如何在应用中针对不同的使用场景使用不同的加密算法,你的应用才是安全的。因此,这篇文章将会介绍平常工作中比较常用的几种加密方式,并给出对应的Node.js代码。

保护密码

用户在注册应用的时候难免会使用一些过于简单的密码,同时也倾向于在不同的站点使用相同的密码。最可怕的是用来存储用户密码的数据库可能被黑客侵入。因此,对于用户密码的保护是最基础的防护措施。(ps: 大家可以使用这个网站来检查自己的密码是否被泄露了。)

对于密码的加密,通常使用的是哈希(hash)算法。比较常见的哈希算法有Argon2、PBKDF2、scrypt和bcrypt等。关于这些加密算法的介绍,大家可以参考Password Storage Cheat Sheet这篇文章。

node.js中提供了crypto模块,该模块主要用于实现基础的加解密算法。接下来就来介绍一下如何使用该模块来实现对于密码的保护:

// 这里是最基础的md5算法
const crypto = require('crypto');

const hash = crypto.createHash('md5'); // 最基本的md5算法

hash.update('password1'); // update用来更新数据

hash.digest('hex'); // 以string格式输出密文
hash.digest('sha256');

对于md5算法,这里推荐使用https://hashtoolkit.com/这个网站进行调试。普通的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'));

保护存储数据

对称加密算法(Symmetric Encryption)是一种使用同一密钥进行加密和解密文本的算法,这也意味着通信双方要使用同一密钥进行加解密。对称加密算法对于大型数据的加密来说速度很快,因此它也主要用于存储数据的加密。比较常用的对称加密算法有AES、Blowfish、DES和RC4等。

crypto模块中主要利用以下方法来实现对称加密:

  • createCipheriv: 提供对称加密, 该方法接收三个参数,第一个是加密算法、第二个是密钥,而第三个是初始向量(initialization vector)

  • update 和 final: 先更新数据,然后获得密文

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)

同时用于对称加密的密钥也需要有合适的密码管理策略进行处理,否则一旦密钥被破解,数据的安全性就无从保障。通常我们会使用一个密钥管理系统(KMS)进行管理密钥。所有的加密密钥都由该系统进行统一分配和管理,而用于数据加密的密钥由一个主密钥(master
key)进行加密。比较流行的KMS有https://cloud.google.com/kms/https://aws.amazon.com/kms/,当然你可以选择自己实现。

保护通信信道

存储数据的保护使用的是对称算法,与之相对应的非对称算法则主要用于保护通信信道。

const crypto = require('crypto');

// 加密方法
exports.encrypt = (data, key) => {
  // 公钥加密
  return crypto.publicEncrypt(key, Buffer.from(data));
};

// 解密方法
exports.decrypt = (encrypted, key) => {
  // 私钥解密
  return crypto.privateDecrypt(key, encrypted);
};

在数据传输过程中,我们还会使用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)是一种组合使用两种不同的验证机制来确认用户身份的机制。主要是通过手机等设备来生成token,具体到代码实现层面,可以在服务端和客户端之间生成一个临时的代码序列来校验。

这里推荐使用speakeasy这个npm库来实现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');

总结

这篇文章主要介绍了如何在Node.js中使用crypto模块来保护密码、存储数据和通信信道。最后还介绍了如何使用speakeasy模块来实现双因子验证机制,希望这篇文章对大家有所帮助。

参考资料

Crypto In Node.js

Zero to Hashing in Under 10 Minutes: Argon2 in Nodejs – Hunter2: AppSec Training

Symmetric Encryption in JavaScript - Ekene Izukanne - Medium

深入理解双因子认证

Practical Cryptography in Node.js

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,219评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,363评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,933评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,020评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,400评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,640评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,896评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,597评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,327评论 1 244
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,581评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,072评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,399评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,054评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,849评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,672评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,585评论 2 270