LoginSignup
7
3

More than 3 years have passed since last update.

【Go言語】パスワード入力モード中断時にターミナルで入力文字が非表示になってしまう問題の解決

Posted at

何がしたかったのか

以下のように、のぞき見に耐性をつけるために、入力中のパスワードを非表示にすることがある。

$ ./command #何らかのコマンド
Enter password:             # P@ssw0rd を入力
Your password is 'P@ssw0rd'

これをGo言語で実装したい。
ということで、調べてみると意外とすぐに見つかる。
おそらくすぐに出てくるのは、以下のコード。

package main

import (
    "fmt"
    "syscall"

    "golang.org/x/crypto/ssh/terminal"
)

func main() {
    fmt.Print("Enter password: ")
    pwd, err := terminal.ReadPassword(syscall.Stdin) // ここ一行で実装可能
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println()
    fmt.Println(string(pwd))
}

すごくシンプルで簡単だが、一つ罠がある。

パスワード入力画面で Ctrl + Cなどで中断した際に、その後の ターミナル上での入力がすべて非表示状態になってしまう

$ ./command # 何らかのコマンド
Enter password:    # 入力途中で Ctrl+C
^Csignal: interrupt
$ # 何を打っても表示されない

なのでこれを解決しよう。

解決策

解決方法としては、Ctrl+Cのシグナルをキャプチャして、シグナルを受信した場合、ターミナルの非表示状態を解除すればよい。
実装すると以下のコードになる。

package main

import (
    "os"
    "os/signal"
    "syscall"

    "golang.org/x/crypto/ssh/terminal"
)

func ReadPassword() ([]byte, error) {
    // Ctrl+Cのシグナルをキャプチャする
    signalChan := make(chan os.Signal)
    signal.Notify(signalChan, os.Interrupt)
    defer signal.Stop(signalChan)

    // 現在のターミナルの状態をコピーしておく
    currentState, err := terminal.GetState(int(syscall.Stdin))
    if err != nil {
        return nil, err
    }

    go func() {
        <-signalChan
        // Ctrl+Cを受信後、ターミナルの状態を先ほどのコピーを用いて元に戻す
        terminal.Restore(int(syscall.Stdin), currentStates)
        os.Exit(1)
    }()

    return terminal.ReadPassword(syscall.Stdin)
}

たったこれだけ。考えると単純だが、最初詰まった時は困った。

参考

7
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
3