我们正在使用bcrypt来获取永远不需要解密的密码和数据。
应采取哪些措施来保护其他用户信息。对于这个例子,我们可以说,如果有人要获取数据库,我们不希望用户的真实姓名是纯文本。
这是一些有些敏感的数据,但也需要不时调用并以纯文本显示。有一种简单的方法可以做到这一点吗?
答案 0 :(得分:112)
您可以使用crypto模块:
var crypto = require('crypto');
var assert = require('assert');
var algorithm = 'aes256'; // or any other algorithm supported by OpenSSL
var key = 'password';
var text = 'I love kittens';
var cipher = crypto.createCipher(algorithm, key);
var encrypted = cipher.update(text, 'utf8', 'hex') + cipher.final('hex');
var decipher = crypto.createDecipher(algorithm, key);
var decrypted = decipher.update(encrypted, 'hex', 'utf8') + decipher.final('utf8');
assert.equal(decrypted, text);
答案 1 :(得分:10)
已弃用@mak答案crypto.createCipher
和crypto.createDecipher
的更新。最新的工作代码为:
var crypto = require("crypto");
var algorithm = "aes-192-cbc"; //algorithm to use
var password = "Hello darkness";
const key = crypto.scryptSync(password, 'salt', 24); //create key
var text= "this is the text to be encrypted"; //text to be encrypted
const iv = crypto.randomBytes(16); // generate different ciphertext everytime
const cipher = crypto.createCipheriv(algorithm, key, iv);
var encrypted = cipher.update(text, 'utf8', 'hex') + cipher.final('hex'); // encrypted text
const decipher = crypto.createDecipheriv(algorithm, key, iv);
var decrypted = decipher.update(encrypted, 'hex', 'utf8') + decipher.final('utf8'); //deciphered text
console.log(decrypted);
答案 2 :(得分:6)
2019年1月23日更新
解密逻辑中的错误已修复。感谢@AlexisWilke正确指出了这一点。
公认的答案已有7年历史,而且今天看起来还不太安全。因此,我正在回答:
加密算法:具有256位密钥的分组密码AES被认为足够安全。要加密完整的消息,需要选择一种模式。建议使用经过身份验证的加密(同时提供机密性和完整性)。 GCM,CCM和EAX是最常用的经过身份验证的加密模式。 GCM通常是首选,并且在提供GCM专用说明的英特尔体系结构中表现良好。所有这三种模式都是基于CTR(基于计数器)的模式,因此它们不需要填充。因此,它们不容易受到与填充相关的攻击
GCM需要初始化向量(IV)。 IV不是秘密。唯一的要求是它必须是随机的或不可预测的。在NodeJ中,crypto.randomBytes()
用于产生加密强度高的伪随机数。
NIST建议GCM使用96位IV,以提高互操作性,效率和简化设计
收件人需要知道IV才能解密密文。因此,IV需要与密文一起传输。一些实现将IV作为AD(关联数据)发送,这意味着将在密文和IV上计算身份验证标签。但是,这不是必需的。可以在IV前面简单地加上密文,因为如果IV在传输过程中由于故意的攻击或网络/文件系统错误而被更改,则身份验证标签验证仍然会失败
字符串不应该用于保存明文消息,密码或密钥,因为字符串是不可变的,这意味着使用后我们无法清除字符串,它们会在内存中徘徊。因此,内存转储可以显示敏感信息。出于同样的原因,调用这些加密或解密方法的客户端应在不再需要使用Buffer
之后清除所有保存消息,密钥或密码的bufferVal.fill(0)
。
最后,要通过网络或存储进行传输,应使用Base64编码对密文进行编码。 buffer.toString('base64');
可用于将Buffer
转换为Base64编码的字符串。
请注意,密钥派生scrypt(crypto.scryptSync()
)已用于从密码派生密钥。但是,此功能仅在Node 10. *和更高版本中可用
代码在这里:
const crypto = require('crypto');
var exports = module.exports = {};
const ALGORITHM = {
/**
* GCM is an authenticated encryption mode that
* not only provides confidentiality but also
* provides integrity in a secured way
* */
BLOCK_CIPHER: 'aes-256-gcm',
/**
* 128 bit auth tag is recommended for GCM
*/
AUTH_TAG_BYTE_LEN: 16,
/**
* NIST recommends 96 bits or 12 bytes IV for GCM
* to promote interoperability, efficiency, and
* simplicity of design
*/
IV_BYTE_LEN: 12,
/**
* Note: 256 (in algorithm name) is key size.
* Block size for AES is always 128
*/
KEY_BYTE_LEN: 32,
/**
* To prevent rainbow table attacks
* */
SALT_BYTE_LEN: 16
}
const getIV = () => crypto.randomBytes(ALGORITHM.IV_BYTE_LEN);
exports.getRandomKey = getRandomKey = () => crypto.randomBytes(ALGORITHM.KEY_BYTE_LEN);
/**
* To prevent rainbow table attacks
* */
exports.getSalt = getSalt = () => crypto.randomBytes(ALGORITHM.SALT_BYTE_LEN);
/**
*
* @param {Buffer} password - The password to be used for generating key
*
* To be used when key needs to be generated based on password.
* The caller of this function has the responsibility to clear
* the Buffer after the key generation to prevent the password
* from lingering in the memory
*/
exports.getKeyFromPassword = getKeyFromPassword = (password, salt) => {
return crypto.scryptSync(password, salt, ALGORITHM.KEY_BYTE_LEN);
}
/**
*
* @param {Buffer} messagetext - The clear text message to be encrypted
* @param {Buffer} key - The key to be used for encryption
*
* The caller of this function has the responsibility to clear
* the Buffer after the encryption to prevent the message text
* and the key from lingering in the memory
*/
exports.encrypt = encrypt = (messagetext, key) => {
const iv = getIV();
const cipher = crypto.createCipheriv(
ALGORITHM.BLOCK_CIPHER, key, iv, { 'authTagLength': ALGORITHM.AUTH_TAG_BYTE_LEN });
let encryptedMessage = cipher.update(messagetext);
encryptedMessage = Buffer.concat([encryptedMessage, cipher.final()]);
return Buffer.concat([iv, encryptedMessage, cipher.getAuthTag()]);
}
/**
*
* @param {Buffer} ciphertext - Cipher text
* @param {Buffer} key - The key to be used for decryption
*
* The caller of this function has the responsibility to clear
* the Buffer after the decryption to prevent the message text
* and the key from lingering in the memory
*/
exports.decrypt = decrypt = (ciphertext, key) => {
const authTag = ciphertext.slice(-16);
const iv = ciphertext.slice(0, 12);
const encryptedMessage = ciphertext.slice(12, -16);
const decipher = crypto.createDecipheriv(
ALGORITHM.BLOCK_CIPHER, key, iv, { 'authTagLength': ALGORITHM.AUTH_TAG_BYTE_LEN });
decipher.setAuthTag(authTag);
let messagetext = decipher.update(encryptedMessage);
messagetext = Buffer.concat([messagetext, decipher.final()]);
return messagetext;
}
下面还提供了单元测试:
const assert = require('assert');
const cryptoUtils = require('../lib/crypto_utils');
describe('CryptoUtils', function() {
describe('decrypt()', function() {
it('should return the same mesage text after decryption of text encrypted with a randomly generated key', function() {
let plaintext = 'my message text';
let key = cryptoUtils.getRandomKey();
let ciphertext = cryptoUtils.encrypt(plaintext, key);
let decryptOutput = cryptoUtils.decrypt(ciphertext, key);
assert.equal(decryptOutput.toString('utf8'), plaintext);
});
it('should return the same mesage text after decryption of text excrypted with a key generated from a password', function() {
let plaintext = 'my message text';
/**
* Ideally the password would be read from a file and will be in a Buffer
*/
let key = cryptoUtils.getKeyFromPassword(Buffer.from('mysecretpassword'), cryptoUtils.getSalt());
let ciphertext = cryptoUtils.encrypt(plaintext, key);
let decryptOutput = cryptoUtils.decrypt(ciphertext, key);
assert.equal(decryptOutput.toString('utf8'), plaintext);
});
});
});
答案 3 :(得分:1)
这是the answer发布的Saptarshi Basu的简化版:
更改:
Buffer
模块明确导入buffer
let
变量转换为const
变量(或完全省略)module.exports
转换为单个对象exports.x = x = (...)
声明移至module.exports
对象ALGORITHM
对象的文档代码:
const crypto = require("crypto");
const { Buffer } = require("buffer");
const ALGORITHM = {
// GCM is an authenticated encryption mode that not only provides confidentiality but also provides integrity in a secured way
BLOCK_CIPHER: "aes-256-gcm",
// 128 bit auth tag is recommended for GCM
AUTH_TAG_BYTE_LEN: 16,
// NIST recommends 96 bits or 12 bytes IV for GCM to promote interoperability, efficiency, and simplicity of design
IV_BYTE_LEN: 12,
// NOTE: 256 (in algorithm name) is key size (block size for AES is always 128)
KEY_BYTE_LEN: 32,
// to prevent rainbow table attacks
SALT_BYTE_LEN: 16
};
module.exports = {
getRandomKey() {
return crypto.randomBytes(ALGORITHM.KEY_BYTE_LEN);
},
// to prevent rainbow table attacks
getSalt() {
return crypto.randomBytes(ALGORITHM.SALT_BYTE_LEN);
},
/**
*
* @param {Buffer} password - The password to be used for generating key
*
* To be used when key needs to be generated based on password.
* The caller of this function has the responsibility to clear
* the Buffer after the key generation to prevent the password
* from lingering in the memory
*/
getKeyFromPassword(password, salt) {
return crypto.scryptSync(password, salt, ALGORITHM.KEY_BYTE_LEN);
},
/**
*
* @param {Buffer} messagetext - The clear text message to be encrypted
* @param {Buffer} key - The key to be used for encryption
*
* The caller of this function has the responsibility to clear
* the Buffer after the encryption to prevent the message text
* and the key from lingering in the memory
*/
encrypt(messagetext, key) {
const iv = crypto.randomBytes(ALGORITHM.IV_BYTE_LEN);
const cipher = crypto.createCipheriv(ALGORITHM.BLOCK_CIPHER, key, iv, {
authTagLength: ALGORITHM.AUTH_TAG_BYTE_LEN
});
let encryptedMessage = cipher.update(messagetext);
encryptedMessage = Buffer.concat([encryptedMessage, cipher.final()]);
return Buffer.concat([iv, encryptedMessage, cipher.getAuthTag()]);
},
/**
*
* @param {Buffer} ciphertext - Cipher text
* @param {Buffer} key - The key to be used for decryption
*
* The caller of this function has the responsibility to clear
* the Buffer after the decryption to prevent the message text
* and the key from lingering in the memory
*/
decrypt(ciphertext, key) {
const authTag = ciphertext.slice(-16);
const iv = ciphertext.slice(0, 12);
const encryptedMessage = ciphertext.slice(12, -16);
const decipher = crypto.createDecipheriv(ALGORITHM.BLOCK_CIPHER, key, iv, {
authTagLength: ALGORITHM.AUTH_TAG_BYTE_LEN
});
decipher.setAuthTag(authTag);
const messagetext = decipher.update(encryptedMessage);
return Buffer.concat([messagetext, decipher.final()]);
}
};
请记住,尽管经过简化,但该代码在功能上应与Saptarshi Basu的代码相同。
祝你好运。
答案 4 :(得分:1)
虽然这已得到正确回答,但使用加密库的一个好模式是在类包装器中,多年来我已将其复制/粘贴到各种项目中。
const crypto = require("crypto");
class Encrypter {
constructor(encryptionKey) {
this.algorithm = "aes-192-cbc";
this.key = crypto.scryptSync(encryptionKey, "salt", 24);
}
encrypt(clearText) {
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv(this.algorithm, this.key, iv);
const encrypted = cipher.update(clearText, "utf8", "hex");
return [
encrypted + cipher.final("hex"),
Buffer.from(iv).toString("hex"),
].join("|");
}
dencrypt(encryptedText) {
const [encrypted, iv] = encryptedText.split("|");
if (!iv) throw new Error("IV not found");
const decipher = crypto.createDecipheriv(
this.algorithm,
this.key,
Buffer.from(iv, "hex")
);
return decipher.update(encrypted, "hex", "utf8") + decipher.final("utf8");
}
}
// Usage
const encrypter = new Encrypter("secret");
const clearText = "adventure time";
const encrypted = encrypter.encrypt(clearText);
const dencrypted = encrypter.dencrypt(encrypted);
console.log({ worked: clearText === dencrypted });
答案 5 :(得分:-2)
var crypto = require('crypto'),
algorithm = 'aes-256-ctr',
password = 'RJ23edrf';
//Here "aes-256-cbc" is the advance encryption standard we are using for encryption.
function encrypt(text){
var cipher = crypto.createCipher(algorithm,password)
var crypted = cipher.update(text,'utf8','hex')
crypted += cipher.final('hex');
return crypted;
}
function decrypt(text){
var decipher = crypto.createDecipher(algorithm,password)
var dec = decipher.update(text,'hex','utf8')
dec += decipher.final('utf8');
return dec;
}
var salt = uuid.v4()
var e = encrypt();
console.log(e);
var d = decrypt(e);
console.log(d);