【ITニュース解説】Common Lisp: read password on a terminal (by hiding its input)
2025年09月05日に「Dev.to」が公開したITニュース「Common Lisp: read password on a terminal (by hiding its input)」について初心者にもわかりやすいように丁寧に解説しています。
ITニュース概要
Common Lispプログラムで、ターミナルからパスワードを入力する際に入力文字を隠す方法を紹介。Linuxの`stty -echo`コマンドを呼び出して一時的に画面表示をオフにする。これにより、入力内容が画面に表示されなくなる。(114文字)
ITニュース解説
システム開発において、ユーザーからパスワードのような機密情報を入力してもらう機会は多い。しかし、ターミナル(コマンドライン)上でパスワードを入力する際に、入力した文字がそのまま画面に表示されてしまうと、後ろから覗き見られたり、画面を記録されたりするリスクがあり、セキュリティ上の問題となる。このような問題を解決するためには、パスワード入力時に画面に文字が表示されないようにする仕組みが必要である。今回の記事は、Common Lispというプログラミング言語を使って、この「入力文字を隠す」機能をどのように実現するかを解説している。
基本的なアプローチは、ターミナルの「エコー」という機能を一時的に無効にすることだ。エコーとは、キーボードで入力した文字が画面に表示される、普段私たちが当たり前に使っている機能のことである。パスワード入力時にはこのエコー機能をオフにし、入力が完了したらすぐにオンに戻す、という手順で安全な入力を実現する。
エコーのオン・オフを切り替えるために使われるのは、UNIX系のシステム(LinuxやmacOSなど)に標準で備わっているstty("set tty"の略)というコマンドだ。sttyコマンドは、ターミナルのさまざまな設定を操作するためのツールであり、例えばstty -echoと実行するとエコー機能が無効になり、入力文字が表示されなくなる。逆にstty echoと実行すると、エコー機能が再び有効に戻り、通常通り文字が表示されるようになる。
Common Lispでこのような外部コマンドを実行するには、uiop:run-programという関数が利用できる。記事では、この関数を使ってsttyコマンドを実行する二つの補助関数を定義している。一つはdisable-echo関数で、これはstty -echoを実行してターミナルのエコーを無効にする。もう一つはenable-echo関数で、stty echoを実行してエコーを再び有効に戻す役割を果たす。これらの関数は、uiop:run-programに("/bin/stty" "echo")や("/bin/stty" "-echo")のようにコマンドとその引数をリスト形式で渡し、さらに:input :interactive :output :interactiveというオプションを指定している。これは、実行するコマンドが標準入力や標準出力と対話的に動作することを意味する。
実際のパスワード入力を処理する部分がpass-prompt関数だ。この関数の中では、まずformat t "password: "というコードで「password: 」というプロンプトを画面に表示し、force-outputで表示内容が確実に画面に送られるようにしている。これは、出力が一時的にバッファリングされる(一時的にためられる)可能性があるため、明示的に出力させることで、プロンプトがすぐにユーザーに見えるようにするためである。
次に、disable-echoを呼び出してエコー機能を無効にする。これにより、ユーザーがキーボードでパスワードを入力しても、画面には何も表示されなくなる。その後、read-line関数を使って、ユーザーが入力した一行の文字列を読み取る。この間、画面は何も表示しないが、入力されたパスワードはプログラムによって正確に受け取られている。
パスワードの読み取りが完了したら、非常に重要な処理としてenable-echoを呼び出し、エコー機能を再び有効に戻す必要がある。もしこれを忘れてしまうと、パスワード入力後もターミナルのエコーが無効なままとなり、その後に入力するすべての文字が画面に表示されなくなってしまい、非常に使いづらい状態となる。
このエコーを確実に有効に戻すために、Common Lispではunwind-protectという特殊な機能が使われている。unwind-protectは、その本体部分(ここではread-lineでパスワードを読み取る部分)が正常に完了したか、あるいは途中でエラーが発生して終了したかにかかわらず、必ず指定された「クリーンアップ処理」(ここではenable-echoを呼び出す部分)を実行することを保証する。これは、プログラムがどのような状況で終了しても、ターミナルの状態を元の状態に戻すための、堅牢な仕組みである。
記事の最後には、このpass-prompt関数を使ってパスワードを入力させ、その内容を処理する簡単な例も示されている。この例では、ユーザーが入力したパスワードをlet ((pass (pass-prompt)))という部分で受け取り、その後「I still know the pass was "入力されたパスワード"」のように画面に表示している。これにより、入力されたパスワードがプログラム内部で正しく保持されていることを確認できる。
また、記事ではターミナルでの入力だけでなく、他の文脈で機密情報を隠すためのライブラリも紹介している。例えば、secret-valuesやprivacy-output-streamといったライブラリは、デバッグ出力やREPL(Read-Eval-Print Loop、対話型実行環境)の履歴など、ターミナル以外の場所で誤ってパスワードなどの機密情報が表示されてしまうのを防ぐのに役立つ。これは、プログラム内部で扱っている機密情報が、意図しない形でログファイルに残ったり、開発者の画面に表示されたりすることを防ぐためのものであり、セキュリティ意識の高いシステム開発では非常に重要となる側面である。
Common Lispは、このような低レベルなシステム操作から高レベルなアプリケーション開発まで幅広く対応できる強力なプログラミング言語であり、この記事はその柔軟性と実用性の一端を示している。システムエンジニアを目指す上で、このようなセキュリティを考慮したプログラミング手法や、OSの基本的なコマンドを活用する知識は非常に役立つだろう。