-
Notifications
You must be signed in to change notification settings - Fork 0
Design: Encryption of credentials
Target: 0.15
Currently credentials are passed to the storage API in the clear so unless the storage mechanism does anything specific, they get stored in the clear.
With a move to add version control backing to node-red, the very real prospect emerges of credentials being stored, in the clear, in version control. That is highly undesirable.
We cannot escape the fact that we need to store credential information in a retrievable way; hashing is not an option.
This feature is to enable encryption of credentials by default - a user has to explicitly disable encryption if they do not want it to apply.
The encryption scheme requires a key to encrypt/decrypt the content.
A user is able to provide their own key via the credentialSecret property in the settings file. But most users will not do that the first time they run node-red after upgrading to this release. In which case, the runtime will auto-generate a key and store it in runtime settings. The credentials will then get encrypted with that key the next time flows are deployed.
If a user then provides their own credentialSecret property in the settings.js file, the runtime will migrate from the generated key to the user provided key the next time flows are deployed.
If a user changes credentialSecret at any point, the runtime will no longer be able to decrypt the credentials and they will be lost.
The credentials passed over the Storage API will be the encrypted set. An unencrypted credential object looks like this:
{
"nodeId": {
"credentialA": "value",
"credentialB": "value"
}
}
After encryption, it looks like this:
{
"$": "b89d8209b5158a3c313675561b1a5b5phN1gDBe81Z"
}
By keeping it a valid JSON object underlying storage implementations should not be affected by the change.
var encryptionKey = crypto.createHash('sha256').update(userKey).digest();
var initVector = crypto.randomBytes(16);
var cipher = crypto.createCipheriv("aes-256-ctr", encryptionKey, initVector);
var result = cipher.update(JSON.stringify(credentials), 'utf8', 'base64') + cipher.final('base64');
result = initVector.toString('hex') + result;
var encryptionKey = crypto.createHash('sha256').update(userKey).digest();
var initVector = new Buffer(encryptedCredentials.substring(0, 32),'hex');
encryptedCredentials = encryptedCredentials.substring(32);
var decipher = crypto.createDecipheriv(encryptionAlgorithm, encryptionKey, initVector);
var decrypted = decipher.update(encryptedCredentials, 'base64', 'utf8') + decipher.final('utf8');
var result = JSON.parse(decrypted);