Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make a secure password input field in a command line app?

I am writing a command line application for OS X in Swift. I need to prompt for a username and password (not the user's account on the computer and not credentials saved in the Keychain). I can prompt for the username just fine:

func getInput() -> String {
    var keyboard = NSFileHandle.fileHandleWithStandardInput()
    var inputData = keyboard.availableData
    return NSString(data: inputData, encoding:NSUTF8StringEncoding)!.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet())
}

let username = getInput()

I would like to prompt for the password as well but it prints the password in the console as the user types it. How can I accept input from STDIN without having it echoed back to the console?

Please note that I need the application to be interactive, the user will never be passing the username and password as command line arguments, so argv is not the correct answer.

For example, when doing this in Ruby, you would enter this:

require 'io/console'
puts "Enter your password"
@password = STDIN.noecho(&:gets).chomp

And then it would display on the screen as this image:

Hidden password field example

No matter how many letters are entered, it only shows that blue box with the dot in it. It doesn't print the password to the screen and it doesn't show ********* for the password either.

Is it possible to do the same thing with Swift or Objective-C in a command line OS X application?

like image 394
anothermh Avatar asked Feb 19 '15 00:02

anothermh


1 Answers

Just for the sake of completeness: getpass() works great for passwords, but unfortunately this function also has the limitation that it can only read up to 128 characters. If you want to prompt for really long passphrase or private keys, use BSD's readpassphrase() instead.

Swift 2.0 example:

var buf = [Int8](count: 8192, repeatedValue: 0)
let passphrase = readpassphrase("Enter passphrase: ", &buf, buf.count, 0)
if let passphraseStr = String.fromCString(passphrase) {
    print(passphraseStr)
}

Swift 3.1 and later:

var buf = [CChar](repeating: 0, count: 8192)
if let passphrase = readpassphrase("Enter passphrase: ", &buf, buf.count, 0),
    let passphraseStr = String(validatingUTF8: passphrase) {

    print(passphraseStr)
}
like image 173
seb Avatar answered Oct 08 '22 19:10

seb