Table of Contents
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.
Encryption scheme
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;
Decryption scheme
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);
FAQ
The credentials file (flows_cred.json
) is encrypted by default to ensure its contents cannot be easily read.
Node-RED generates a random key for the encryption if you do not provide one in your settings file. If the second instance of Node-RED doesn't have the same encryption key, it won't be able to decrypt the file.
Here are the steps you need to resolve this.
-
edit your
settings.js
file and add acredentialSecret
property with a whatever string value you want. If you want to disable encryption, set its value tofalse
.credentialSecret: "my-random-string"
-
Restart Node-RED and deploy a change - this will trigger Node-RED to re-encrypt your credentials with your chosen key (or disabling encryption if set to false).
-
You can then copy your flow/credential file to a second instance, just make sure you give it the same
credentialSecret
value in its settings file.
Note that once you set credentialSecret
you cannot change its value.