Node: Ciphertext can be decrypted when using array as password

Created on 30 Mar 2018  路  13Comments  路  Source: nodejs/node

Issuetype: bug
Version: v6.14.0
Platform: Linux 4.4.0-116-generic ~14.04.1-Ubuntu SMP x86_64 GNU/Linux
Subsystem: crypto

Hi, I am using crypto module for encryption and decryption.

const { pbkdf2, createCipheriv } = require('crypto');

const encryptData = (plainText, password) => new Promise((resolve, reject) => { 
    const salt = randomBytes(32);
    const iv = randomBytes(16);    

    pbkdf2(password, salt, 100000, 32, 'sha512', (error, derivedKey) => {
        if (error) {
            console.log(error);
            reject(error);
        }
        else {
            let encrypted = '';
            const cipher = createCipheriv('aes-256-ctr', derivedKey, iv);
            encrypted += cipher.update(plainText, 'utf8', 'hex');
            encrypted += cipher.final('hex');
            const data = {
                cipherText: encrypted,
                salt: salt.toString('hex'),
                iv: iv.toString('hex')
            };
            resolve(data);            
        }
    });
});

const decryptData = (cipherText, password, salt, iv) => new Promise((resolve, reject) => {
    const _salt = Buffer.from(salt, 'hex');
    const _iv = Buffer.from(iv, 'hex');
    pbkdf2(password, _salt, 100000, 32, 'sha512', (error, derivedKey) => {
        if (error) {
            reject(error);
        }
        else {
            let decrypted = '';
            const decipher = createCipheriv('aes-256-ctr', derivedKey, _iv);
            decrypted += decipher.update(cipherText, 'hex', 'utf8');
            decrypted += decipher.final('utf8');
            resolve(decrypted);
        }
    });
});

Data that is encrypted: "Hello World"

I think my decryption code is broken in 4th step.

  1. If I use "LOCKIT" as password to encrypt the above string and use the same password to decrypt the cipherText, I can get Hello World back.
  2. If I use a different password "IAMSTEALINGIT",I am getting an error invalid encoded string.
  3. If an array variable to passed as password ["LOCKIT"], I can encrypt and decrypt the data.
  4. The data is encrypted using ["LOCKIT"]. If I try to decrypt the data using [" "], ["SNEAKYTHIEF"], ["ONEPUNCH"] as passwords, I am getting Hello World as the decrypted data.

I don't know whether my code is correct or not. Can someone help me with this issue?

crypto question security wrong repo

Most helpful comment

Juding from the comments above, this looks like it may be a noticeable security footgun in whatever npm package that is responsible for this.

Encrypt functions absolutely mustn't coerce invalid values in passwords to constant strings.

All 13 comments

Could you please post code showing how you invoke these functions? Does this reproduce in recent LTS releases?

Is it possible array passwords are being converted to a (constant) string (e.g. '[object Array]'), which might explain why any array value will work?

@tniessen I am building a react project using webpack. I am using the functions as follows:


import { encryptData, decryptData } from './crypto-methods';

// both the functions are in different components. 
// I have just nested functions to show how I am invoking them
// I am actually saving the data returned by encrypData function in localStorage,
// Later in a different component, I am getting the values from localStorage and decrypting it
encryptData(this.state.message, this.state.password).then(data => {
    console.log(`Encrypted Data: ${JSON.stringify(data)}`); // this goes to localStorage
    decryptData(data.cipherText, this.state.password, data.salt, data.iv).then(data => console.log(data));
});

I made a mistake while updating the textbox value,

handleTextbox_onChange(event){
    this.setState({
         [event.target.name]: [event.target.value]
    })
}

As you can see, instead of passing event.target.value, I was passing [event.target.value]. So when I entered different passwords, I could decrypt the cipherText.

@gmk292 Which CJS crypto module are using in your project? The officially maintained crypto module does not work as part of a client-side JS application.

@tniessen I am using the nodejs crypto module. You are right.

I just tested the same in node console. When array is used I am getting the following error.

UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 2): TypeError: Pass phrase must be a buffer

Sorry for raising an issue, without checking this first.:bow:

To summarize: The problem occurs when passing an array as the "passphrase" parameter to crypto.pbkdf2 in node 6.14.0. Is that correct?

The function should throw an error:

> process.version
'v6.14.0'
> crypto.pbkdf2([ 'LOCKIT' ], crypto.randomBytes(32), 100000, 32, 'sha512', (err, key) => console.log(key));
TypeError: Pass phrase must be a buffer
    at TypeError (native)
    at pbkdf2 (crypto.js:622:20)
    at Object.exports.pbkdf2 (crypto.js:604:10)
    at repl:1:8
    at sigintHandlersWrap (vm.js:22:35)
    at sigintHandlersWrap (vm.js:73:12)
    at ContextifyScript.Script.runInThisContext (vm.js:21:12)
    at REPLServer.defaultEval (repl.js:340:29)
    at bound (domain.js:280:14)
    at REPLServer.runBound [as eval] (domain.js:293:12)

I am using the nodejs crypto module.

So you are using the nodejs crypto modulo on the client-side from within a browser or is the code being executed by a Node.js server application?

@tniessen I am using webpack-dev-server for the react application. It is pure client-side app. There are no services in the backend that do the encryption/decryption for the app

@gmk292 I didn't notice that you already edited https://github.com/nodejs/node/issues/19699#issuecomment-377533195, sorry. As suspected, this is not a problem with Node.js but with the module used within webpack. Please open an issue there.

@tniessen Thanks for your help :smile:

No problem, always glad to be of assistance 馃槂

Juding from the comments above, this looks like it may be a noticeable security footgun in whatever npm package that is responsible for this.

Encrypt functions absolutely mustn't coerce invalid values in passwords to constant strings.

@tniessen Thanks for finding that module and fixing it!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

vsemozhetbyt picture vsemozhetbyt  路  3Comments

Brekmister picture Brekmister  路  3Comments

sandeepks1 picture sandeepks1  路  3Comments

filipesilvaa picture filipesilvaa  路  3Comments

jmichae3 picture jmichae3  路  3Comments