getpasswd functionality in Go?

2019-01-13 14:44发布

Situation:

I want to get a password entry from the stdin console - without echoing what the user types. Is there something comparable to getpasswd functionality in Go?

What I tried:

I tried using syscall.Read, but it echoes what is typed.

9条回答
迷人小祖宗
2楼-- · 2019-01-13 15:23

Here is a solution that I developed using Go1.6.2 that you might find useful.

It only uses the following standard packages: bufio, fmt, os, strings and syscall. More specifically, it uses syscall.ForkExec() and syscall.Wait4() to invoke stty to disable/enable terminal echo.

I have tested it on Linux and BSD (Mac). It will not work on windows.

// getPassword - Prompt for password. Use stty to disable echoing.
import ( "bufio"; "fmt"; "os"; "strings"; "syscall" )
func getPassword(prompt string) string {
    fmt.Print(prompt)

    // Common settings and variables for both stty calls.
    attrs := syscall.ProcAttr{
        Dir:   "",
        Env:   []string{},
        Files: []uintptr{os.Stdin.Fd(), os.Stdout.Fd(), os.Stderr.Fd()},
        Sys:   nil}
    var ws syscall.WaitStatus

    // Disable echoing.
    pid, err := syscall.ForkExec(
        "/bin/stty",
        []string{"stty", "-echo"},
        &attrs)
    if err != nil {
        panic(err)
    }

    // Wait for the stty process to complete.
    _, err = syscall.Wait4(pid, &ws, 0, nil)
    if err != nil {
        panic(err)
    }

    // Echo is disabled, now grab the data.
    reader := bufio.NewReader(os.Stdin)
    text, err := reader.ReadString('\n')
    if err != nil {
        panic(err)
    }

    // Re-enable echo.
    pid, err = syscall.ForkExec(
        "/bin/stty",
        []string{"stty", "echo"},
        &attrs)
    if err != nil {
        panic(err)
    }

    // Wait for the stty process to complete.
    _, err = syscall.Wait4(pid, &ws, 0, nil)
    if err != nil {
        panic(err)
    }

    return strings.TrimSpace(text)
}
查看更多
再贱就再见
3楼-- · 2019-01-13 15:33

You could also use PasswordPrompt function of https://github.com/peterh/liner package.

查看更多
甜甜的少女心
4楼-- · 2019-01-13 15:34

Just saw a mail in #go-nuts maillist. There is someone who wrote quite a simple go package to be used. You can find it here: https://github.com/howeyc/gopass

It something like that:

package main

import "fmt"
import "github.com/howeyc/gopass"

func main() {
    fmt.Printf("Password: ")
    pass := gopass.GetPasswd()
    // Do something with pass
}
查看更多
不美不萌又怎样
5楼-- · 2019-01-13 15:35

you can do this by execing stty -echo to turn off echo and then stty echo after reading in the password to turn it back on

查看更多
欢心
6楼-- · 2019-01-13 15:35

Here is a version specific to Linux:

func terminalEcho(show bool) {
    // Enable or disable echoing terminal input. This is useful specifically for
    // when users enter passwords.
    // calling terminalEcho(true) turns on echoing (normal mode)
    // calling terminalEcho(false) hides terminal input.
    var termios = &syscall.Termios{}
    var fd = os.Stdout.Fd()

    if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd,
        syscall.TCGETS, uintptr(unsafe.Pointer(termios))); err != 0 {
        return
    }

    if show {
        termios.Lflag |= syscall.ECHO
    } else {
        termios.Lflag &^= syscall.ECHO
    }

    if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd,
        uintptr(syscall.TCSETS),
        uintptr(unsafe.Pointer(termios))); err != 0 {
        return
    }
}

So to use it:

fmt.Print("password: ")

terminalEcho(false)
var pw string
fmt.Scanln(&pw)
terminalEcho(true)
fmt.Println("")

It's the TCGETS syscall that is linux specific. There are different syscall values for OSX and Windows.

查看更多
干净又极端
7楼-- · 2019-01-13 15:35

I had a similar usecase and the following code snippet works well for me. Feel free to try this if you are still stuck here.

import (
    "fmt"
    "golang.org/x/crypto/ssh/terminal"

)

func main() {
    fmt.Printf("Now, please type in the password (mandatory): ")
    password, _ := terminal.ReadPassword(0)

    fmt.Printf("Password is : %s", password)
}

Of course, you need to install terminal package using go get beforehand.

查看更多
登录 后发表回答