TL;DR
怖いですよね、セキュリティインシデント。
インフラ系でお仕事をしていると、 Linux にログインして操作する手順書を作る事が多くなります。手順書の中には認証が必要なコマンドを含めなければならない場合もあり、そういった時にはパスワードといったクレデンシャル情報の取り扱いを考える必要性が出てきます。そのような時に最もやってはいけないことは クレデンシャル情報を手順書にべた書きする という行為です。
このような行為は
- 手順書の機密性を不必要に上昇させてしまう
- クレデンシャル情報の更新(例えばアカウントのパスワード変更)の際に漏れなく手順書のべた書き箇所を修正する手間が発生する
という問題をはらむことになります。
そこでよくあるのが、下記のようにコマンドの一部を作業実施時に作業者により読み替えさせる手法です。
$ curl -u user:{パスワード} http://example.com
※ {パスワード} は XXX を参照して読み替えてください。
この手法は手順書の機密性を不必要に上昇させませんし、クレデンシャル情報の参照先を統一しておけばクレデンシャル情報の更新時に参照先を修正するだけでよいので、上記の問題をどちらも解決できます。
しかし、これはベストな手法でしょうか?答えは No です。
それでは、どういった手法を使えばよいのか?というのが本稿の趣旨です。
何が問題か?
読み替え手法には .bash_history にクレデンシャル情報が残ってしまう問題があります。
どういうことか見ていきましょう。
.bash_history にクレデンシャル情報が残ってしまう問題
curl -u user:{パスワード} http://example.com のように、読み替え手法で手順書を記載しても、実際にコマンドを打つ時にはパスワードをダイレクトに入力する必要があります。
.bash_history にどのように記録されるか、実際に読み替え手法でコマンドを入力してみてみましょう。
下記は {パスワード} を password に読み替えた場合の例です。
$ curl -u user:password http://example.com
いったんログアウトしてから再度ログインして ~/.bash_history を見てみると次のように記録されています。
curl -u user:password http://example.com
はい、当たり前ですね。普通に password という文字列が含まれた状態で記録されてしまっています。
実際のところ、普通は .bash_history はオーナーにしか読み書き権限がないので第三者に読み取られる可能性は低いのですが、クレデンシャル情報が平文で保存されてしまうのはいざというときに問題になりかねません。
コマンドをコピペしにくい問題
これは個人的に感じる問題です。
前出の例の curl コマンドであれば、
-
curl -u user:までをコピペ -
{パスワード}に入れるべき実際のパスワードを入力 - パスワード以降の
http://example.comをコピペ(http://example.comの前の半角スペースも含む)
という手順を踏む必要があります。これは意外とストレスです。
更に、たった3ステップではありますが、同じことをいろいろな人間にやらせると必ずミスる人間がでてきます。1
ではどのようにするべきか?
次のように read コマンドを利用すれば解決できます。
$ read -sp "Please input your password: " __pass; echo
※ パスワード は XXX を参照して入力してください。
$ curl -u "user:${__pass}" http://example.com
シェルスクリプトでパスワードの入力を求めるときに read コマンドを使ったことのある方は多いかもしれません。そうです、パスワードを入力するためにその read コマンドを手順書に記載するのです。
read コマンドについて軽くおさらい
read コマンドを実行すると、キーボードからの入力待ち状態になります。オプション等の意味や効果は次の通り。
-
-pオプションでメッセージが指定されていれば、入力待ち状態になる前にそのメッセージを表示します。上記の例ですとPlease input your password:を表示します。 -
-sオプションが指定されていれば、キーボードから入力した内容がエコーバックされません。2 Linux ログイン時にパスワードを入力しても何も画面に表示されないのと同じです。 -
__passは入力された文字列を代入する変数名です。 - 最後の
; echoは改行を出力します。readコマンドは最後に改行を出力しないので、このようにしておかないとプロンプトが現在の行に出力されてしまい不格好になってしまいます。
実際に read コマンドを実行すると次のようになります。入力した値を echo コマンドで表示させています。
$ read -sp "Please input your password: " __pass; echo
Please input your password:
$ echo "$__pass"
mypassword
.bash_history にクレデンシャル情報が残らない
.bash_history にどのように記録されるか、 read コマンド手法のコマンドを入力してみてみましょう。
$ read -sp "Please input your password: " __pass; echo
$ curl -u "user:${__pass}" http://example.com
いったんログアウトしてから再度ログインして ~/.bash_history を見てみると次のように記録されています。
read -sp "Please input your password: " __pass; echo
curl -u "user:${__pass}" http://example.com
read コマンドと curl コマンドの履歴が残りますが、パスワード部分に指定した環境変数が環境変数のまま記録されており、実際に入力されたパスワードが何なのかはコマンド履歴から隠蔽することに成功しています。
read コマンド手法はクレデンシャル情報が .bash_history に記録されるのを見事に防いでいますね。
コマンドのコピペが楽
read コマンド手法ではどのようにコマンドをコピペするのでしょうか。
$ read -sp "Please input your password: " __pass; echo
※ パスワード は XXX を参照して入力してください。
$ curl -u "user:${__pass}" http://example.com
この手順の場合、実際にコマンドを打つ時の流れは
-
read -sp "Please input your password: " __pass; echoを1行まるっとコピペ。 - パスワードを入力。
-
curl -u "user:${__pass}" http://example.comを1行まるっとコピペ。
となります。先に出した例と同じ3ステップですね。読み替え手法と比べて何が良くなったのでしょうか?
...答えはコピペの粒度です。先に出した例だと、1行の中で途中まで/途中からコピペする必要があったのに対して、こちらの例だと1行まるっとコピペするだけになり、ヒューマンエラーの低減を期待できます。3
その他
パスワードの繰り返し入力が不要で楽
これはセキュリティ的なメリットではありませんが、作業時間を短縮できるメリットがあります。
パスワードを環境変数に格納するので、同じパスワードを何度も使う場合に都度入力する必要がありません。例えば次のように使えます。
$ read -sp "Please input your password: " __pass; echo
Please input your password:
$ curl -u "user:${__pass}" http://example.com/file01 -o file01
$ curl -u "user:${__pass}" http://example.com/file02 -o file02
$ curl -u "user:${__pass}" http://example.com/file03 -o file03
パスワードは3か所で使っていますが、入力しているのは read コマンド実行時の一度のみです。
read コマンドを使わなくても大丈夫なコマンドもある
例えば mysql コマンドです。 mysql コマンドは -p オプションでパスワードを指定することができます。
$ mysql -uroot -pmysqlpassword
上記のようにコマンドラインでパスワードを指定する事もできますが、 -p オプションの後にパスワードを指定しないとインタラクティブにパスワードを聞いてきます。
$ mysql -uroot -p
Enter password:
もちろんパスワードのエコーバックもありません。
インタラクティブにパスワードを問い合わせてくるコマンドは、ほとんどの場合パスワードに間違いがあれば直ぐに認証エラーを返してくれ、入力ミスに気づきやすいのでそのまま使った方が良いことが多いです。
まとめ
- パスワード等のクレデンシャル情報を別に安全に管理し、手順書はそれを参照させる
- パスワード等のクレデンシャル情報をインタラクティブに問い合わせてくるコマンドであれば、それを利用する
- コマンド実行時にオプションや引数で指定しなければならないコマンドであれば、
readコマンドを利用する