Как скрыть пароль в консоли nodejs?

Я хочу скрыть ввод пароля. Я вижу много ответов в stackoverflow, но я не могу проверить значение, если я нажимаю backspace. Условие возвращает false.

Я попробовал несколько решений для перезаписывания функции, но у меня возникла проблема с буфером, если я нажимаю backspace, у меня есть невидимый символ \b.

Я нажимаю: "A", backspace, "B", у меня в моем буфере это: "\ u0041\u0008\u0042" (toString() = 'A\bB'), а не "B".

У меня есть:

var readline = require('readline');

var rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});

rl.question("password : ", function(password) {
    console.log("Your password : " + password);
});

Ответ 1

Перезаписать _writeToOutput интерфейса readline приложения: https://github.com/nodejs/node/blob/v9.5.0/lib/readline.js#L291

Чтобы скрыть ввод пароля, вы можете использовать:

ПЕРВОЕ РЕШЕНИЕ: "пароль: [= -]"

Это решение имеет анимацию при нажатии касания:

password : [-=]
password : [=-]

Код:

var readline = require('readline');

var rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});

rl.stdoutMuted = true;

rl.query = "Password : ";
rl.question(rl.query, function(password) {
  console.log('\nPassword is ' + password);
  rl.close();
});

rl._writeToOutput = function _writeToOutput(stringToWrite) {
  if (rl.stdoutMuted)
    rl.output.write("\x1B[2K\x1B[200D"+rl.query+"["+((rl.line.length%2==1)?"=-":"-=")+"]");
  else
    rl.output.write(stringToWrite);
};

Эта последовательность "\ x1B [2K\x1BD" использует две последовательности escape:

  • Esc [2K: очистить всю строку.
  • Esc D: переместить/прокрутить окно вверх на одну строку.

Чтобы узнать больше, прочитайте это: http://ascii-table.com/ansi-escape-sequence-vt-100.php

ВТОРОЕ РЕШЕНИЕ: "пароль: ****"

var readline = require('readline');

var rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});

rl.stdoutMuted = true;

rl.question('Password: ', function(password) {
  console.log('\nPassword is ' + password);
  rl.close();
});

rl._writeToOutput = function _writeToOutput(stringToWrite) {
  if (rl.stdoutMuted)
    rl.output.write("*");
  else
    rl.output.write(stringToWrite);
};

Вы можете очистить историю с:

rl.history = rl.history.slice(1);

Ответ 2

Это можно обрабатывать с помощью readline путем перехвата вывода через приглушенный поток, как это делается в проекте чтения на npm (https://github.com/isaacs/read/blob/master/lib/read.js):

var readline = require('readline');
var Writable = require('stream').Writable;

var mutableStdout = new Writable({
  write: function(chunk, encoding, callback) {
    if (!this.muted)
      process.stdout.write(chunk, encoding);
    callback();
  }
});

mutableStdout.muted = false;

var rl = readline.createInterface({
  input: process.stdin,
  output: mutableStdout,
  terminal: true
});

rl.question('Password: ', function(password) {
  console.log('\nPassword is ' + password);
  rl.close();
});

mutableStdout.muted = true;

Ответ 3

Вы можете использовать модуль readline-sync вместо node readline.

Функция скрытия пароля встроена через опцию "hideEchoBack".

https://www.npmjs.com/package/readline-sync

Ответ 4

Требуется добавить к отмеченному решению №2.

Когда мы обнаруживаем концы строк, я считаю, что мы должны удалить обработчик событий, а не просто stdin.pause(). Это может быть проблемой, если вы ждете от rl.question/rl.prompt в другом месте. В тех случаях, если был использован stdin.pause(), он просто выходил из программы без каких-либо ошибок и может быть довольно неприятным для отладки.

function hidden(query, callback) {
    var stdin = process.openStdin();
    var onDataHandler = function(char) {
        char = char + "";
        switch (char) {
          case "\n": case "\r": case "\u0004":
            // Remove this handler
            stdin.removeListener("data",onDataHandler); 
            break;//stdin.pause(); break;
          default:
            process.stdout.write("\033[2K\033[200D" + query + Array(rl.line.length+1).join("*"));
          break;
        }
    }
    process.stdin.on("data", onDataHandler);

    rl.question(query, function(value) {
      rl.history = rl.history.slice(1);
      callback(value);
    });
}

Ответ 5

Мое решение, скремблированное из разных битов онлайн:

import readline from 'readline';

export const hiddenQuestion = query => new Promise((resolve, reject) => {
  const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
  });
  const stdin = process.openStdin();
  process.stdin.on('data', char => {
    char = char + '';
    switch (char) {
      case '\n':
      case '\r':
      case '\u0004':
        stdin.pause();
        break;
      default:
        process.stdout.clearLine();
        readline.cursorTo(process.stdout, 0);
        process.stdout.write(query + Array(rl.line.length + 1).join('*'));
        break;
    }
  });
  rl.question(query, value => {
    rl.history = rl.history.slice(1);
    resolve(value);
  });
});

Использование выглядит так:

// import { hiddenQuestion } from './hidden-question.js';

const main = async () => {
  console.log('Enter your password and I will tell you your password! ');
  const password = await hiddenQuestion('> ');
  console.log('Your password is "' + password + '". ');
};

main().catch(error => console.error(error));

Ответ 6

Также можно использовать tty.ReadStream
режим изменения process.stdin
для отключения эхо-символов ввода.

let read_Line_Str = "";
let credentials_Obj = {};
process.stdin.setEncoding('utf8');
process.stdin.setRawMode( true );
process.stdout.write( "Enter password:" ); 
process.stdin.on( 'readable', () => {
  const chunk = process.stdin.read();
  if ( chunk !== null ) {
    read_Line_Str += chunk;
    if( 
      chunk == "\n" ||
      chunk == "\r" ||
      chunk == "\u0004"
    ){
      process.stdout.write( "\n" );
      process.stdin.setRawMode( false );
      process.stdin.emit('end'); /// <- this invokes on.end
    }else{
      // providing visual feedback
      process.stdout.write( "*" );  
    }  
  }else{
    //console.log( "readable data chunk is null|empty" );
  }
} );
process.stdin.on( 'end', () => {
  credentials_Obj.user = process.env.USER;
  credentials_Obj.host = 'localhost';
  credentials_Obj.database = process.env.USER;
  credentials_Obj.password = read_Line_Str.trim();
  credentials_Obj.port = 5432;
  //
  connect_To_DB( credentials_Obj );
} );