How to hide password input from terminal in ruby script

前端 未结 6 1765
囚心锁ツ
囚心锁ツ 2020-11-30 20:50

I am new to ruby. I need to receive password as an input through gets command.

How do I hide the password input typed in the terminal, during gets

相关标签:
6条回答
  • 2020-11-30 21:05

    One can also use core ruby.

    $ ri IO.noecho
    
     (from ruby core)
     ------------------------------------------------------------------------------
       io.noecho {|io| }
      ------------------------------------------------------------------------------
    
     Yields self with disabling echo back.
    
       STDIN.noecho(&:gets)
    
     will read and return a line without echo back.
    

    For 1.9.3 (and above), this requires you adding require 'io/console' to your code.

    require 'io/console'
    text = STDIN.noecho(&:gets)
    
    0 讨论(0)
  • 2020-11-30 21:07

    Starting from Ruby version 2.3.0 you can use the IO#getpass method like this:

    require 'io/console'
    password = STDIN.getpass("Password:")
    

    See the getpass method in the Standard Library Documentation.

    0 讨论(0)
  • 2020-11-30 21:12

    As the others mention, you can use IO#noecho for Ruby >= 1.9. If you'd like support for 1.8, you can shell out to the read builtin shell function:

    begin
      require 'io/console'
    rescue LoadError
    end
    
    if STDIN.respond_to?(:noecho)
      def get_password(prompt="Password: ")
        print prompt
        STDIN.noecho(&:gets).chomp
      end
    else
      def get_password(prompt="Password: ")
        `read -s -p "#{prompt}" password; echo $password`.chomp
      end
    end
    

    Now getting a password is as easy as:

    @password = get_password("Enter your password here: ")
    

    Note: In the implementation using read above, you'll run into trouble if you (or some other client of get_password) passes along special shell characters in the prompt (e.g. $/"/'/etc). Ideally, you should escape the prompt string before passing it along to the shell. Unfortunately, Shellwords isn't available in Ruby 1.8. Fortunately, it's easy to backport the relevant bits yourself (specifically shellescape). With that, you can make this slight modification:

      def get_password(prompt="Password: ")
        `read -s -p #{Shellwords.shellescape(prompt)} password; echo $password`.chomp
      end
    

    I mentioned a couple problems with the use of read -s -p in a comment below:

    Well, the 1.8 case is a little janky; it doesn't allow for backslashes, unless you hit backslash twice: "The backslash character `\' may be used to remove any special meaning for the next character read and for line continuation." Also: "The characters in the value of the IFS variable are used to split the line into words. " This should probably be fine for most little scripts, but you'd probably want something more robust for larger applications.

    We can fix some of those problems by rolling up our sleeves and doing this the hard with stty(1). An outline of what we need to do:

    • Store the current terminal settings
    • Turn of echoing
    • Print the prompt & get user input
    • Restore the terminal settings

    We must also take care to restore the terminal settings when interrupted by signals and/or exceptions. The following code will properly handle job control signals (SIGINT/SIGTSTP/SIGCONT) while still playing nicely with any present signal handlers:

    require 'shellwords'
    def get_password(prompt="Password: ")
      new_sigint = new_sigtstp = new_sigcont = nil
      old_sigint = old_sigtstp = old_sigcont = nil
    
      # save the current terminal configuration
      term = `stty -g`.chomp
      # turn of character echo
      `stty -echo`
    
      new_sigint = Proc.new do
        `stty #{term.shellescape}`
        trap("SIGINT",  old_sigint)
        Process.kill("SIGINT", Process.pid)
      end
    
      new_sigtstp = Proc.new do
        `stty #{term.shellescape}`
        trap("SIGCONT", new_sigcont)
        trap("SIGTSTP", old_sigtstp)
        Process.kill("SIGTSTP", Process.pid)
      end
    
      new_sigcont = Proc.new do
        `stty -echo`
        trap("SIGCONT", old_sigcont)
        trap("SIGTSTP", new_sigtstp)
        Process.kill("SIGCONT", Process.pid)
      end
    
      # set all signal handlers
      old_sigint  = trap("SIGINT",  new_sigint)  || "DEFAULT"
      old_sigtstp = trap("SIGTSTP", new_sigtstp) || "DEFAULT"
      old_sigcont = trap("SIGCONT", new_sigcont) || "DEFAULT"
    
      print prompt
      password = STDIN.gets.chomp
      puts
    
      password
    ensure
      # restore term and handlers
      `stty #{term.shellescape}`
    
      trap("SIGINT",  old_sigint)
      trap("SIGTSTP", old_sigtstp)
      trap("SIGCONT", old_sigcont)
    end
    
    0 讨论(0)
  • 2020-11-30 21:17

    There is a library called highline which works like this:

    require 'rubygems'
    require 'highline/import'
    
    password = ask("Enter password: ") { |q| q.echo = false }
    # do stuff with password
    
    0 讨论(0)
  • 2020-11-30 21:17

    For ruby version 1.8 (or Ruby < 1.9) I used read shell builtin as mentioned by @Charles.

    Putting the code thats just enough to prompt for user name & password, where user name will be echoed to screen while typing in however password typed in would be silent.

     userid = `read -p "User Name: " uid; echo $uid`.chomp
     passwd = `read -s -p "Password: " password; echo $password`.chomp
    
    0 讨论(0)
  • 2020-11-30 21:21

    Best method from @eclectic923's answer:

    require 'io/console'
    password = STDIN.noecho(&:gets).chomp
    

    For 1.9.3 (and above), this requires you adding require 'io/console' to your code.

    Original Answer:

    Ruby "Password" is another alternative.

    0 讨论(0)
提交回复
热议问题