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('');
    });
});

// 认证页面
router.get('/authenticate', function(req, res){

    res.send('
Enter Token:
'); }); //校验用户输入 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('

Your token has been verified!

'); } else { console.log('verification failed'); res.send('

verification failed

'); } }); 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

你可能感兴趣的:(Node.js应用中常用安全保护机制和加密算法实现)