Ubuntu Weekly Recipe

第508回Ubuntuでコマンドラインからワンタイムパスワードを扱う

ワンタイムパスワードとは?

様々なWebサービスを利用するのが当たり前となった現在、パスワードの管理は頭を悩ます問題だと思います。本連載でも、第412回ではWebベースのパスワードストアアプリとしてRatticWebを紹介しました。

そもそも固定パスワードには、試行に十分な時間をかけることが可能であればいつかは突破できてしまうという、本質的な問題があります。そこで有効期限が極端に短い、一度きりのパスワードを使い捨てにする方法が考えられました。こういったパスワードをワンタイムパスワードと呼びます。

昨今のWebサービスでは通常のIDとパスワードにくわえ、追加でワンタイムパスワードを入力して認証する「二要素認証」の利用が一般的になってきています。ワンタイムパスワードには様々な方式がありますが、一般的なのはTime-Based One-Time Password Algorithm(TOTP)を利用したものでしょう。代表的なアプリにGoogle Authenticatorがあり、Googleはもちろん、AmazonやFacebookなどのWebサービスの認証に利用できるため、このアプリを活用している人も多いのではないでしょうか[1]⁠。

とはいえ、Webサービスにログインする度にスマホを取り出し、表示される数字を手で打ち込むのは面倒ですよね。そこでCLIからワンタイムパスワードを扱えるツールを紹介します。

oathtoolでワンタイムパスワードを生成する

TOTPの仕組みを簡単に説明すると、サーバーとクライアントでシークレットをあらかじめ共有しておき、現在の時刻をベースに双方でハッシュを生成して照合しています。シークレットはQRコードかbase32の文字列で提供されます。二要素認証を有効にしたことがある人は、スマホにQRコードを読み込ませたことを覚えているのではないでしょうか。

図1 QRコードとテキストでシークレットが提供されているFacebookの二要素認証の設定画面。通常はGoogle AuthenticatorのカメラでQRコードを読み込むが、手動でシークレットを打ち込むこともできる。今回はコマンドに入力する都合上、シークレットの文字列を控えておく。
画像

「oathtool」コマンドを使うと、base32文字列からワンタイムパスワードを生成できます。まずはoathtoolパッケージをインストールしてください。

oathtoolパッケージのインストール
$ sudo apt install oathtool

以下のようにoathtoolコマンドの引数にシークレットを与えて実行すると、6桁のワンタイムパスワードが標準出力に表示されます。

Google Authenticator同様の6桁のワンタイムパスワードを生成する例
$ oathtool --totp -d 6 --time-step-size=30s --base32 (base32のシークレット文字列)

「--totp」オプションは、Time-Based One-Time Password Algorithmを利用するオプションです。oathtoolのデフォルトはHMAC-based One-time Password Algorithmのため、TOTPを使う場合はこのオプションが必要です。

「-d 6」は6桁のワンタイムパスワードを生成するという意味です。6、7、8桁のみがサポートされています。なお6がデフォルト値のため、普段は省略して構いません。

「--time-step-size=30s」はTOTPの有効期間を30秒にするという意味です。デフォルトが30秒になっているためこちらも省略して構いません。なぜ30秒なのかといえば、RFC 6238で「We RECOMMEND a default time-step size of 30 seconds. This default value of 30 seconds is selected as a balance between security and usability.」とされているためです。実際Google Authenticatorでも30秒ごとに新しいパスワードに切り替わるようになっています。

端末からいちいちパスワードをコピーするのは面倒ですので、以下のようにxclipをパイプで繋いで、クリップボードにパスワードを直接流し込んでしまうのがよいでしょう。あとはWebサービスのログイン画面にペーストするだけです。

生成したワンタイムパスワードをクリップボードにコピーする
$ oathtool --totp --base32 (base32のシークレット文字列) | xclip -sel c

「次」のパスワードを表示するには

ワンタイムパスワードは30秒ごとに切り替わるため、キーボードから入力しているうちにパスワードの有効期限が切れてしまったり、Google Authenticatorのカウントダウンを見ながら新しいパスワードが生成されるのを待ったりした経験は誰にでもあると思います。このような時、今のパスワードの有効期限が切れた後、次に生成されるパスワードが事前に見られたら便利ですよね。

oathtoolでは「-w」オプションを指定することで、ステップごとに生成されるパスワードを複数同時に生成できます。たとえば「-w 3」を指定すると、現在時刻をベースにしたパスワードの他に、30秒後、60秒後、90秒後に生成される3つのパスワードを同時に表示します。

複数個のワンタイムパスワードを連続して生成する例
$ oathtool --totp -w 3 --base32 (base32のシークレット文字列)
123456
789123
112233
445566

passを使ってシークレットを管理する

oathtoolでワンタイムパスワードが生成できることはわかりました。しかしここで気づくはずです。一番大切なシークレットを安全に保管するにはどうしたらよいのだろう、と。

シークレットはランダムな長い文字列の形をしていますから、頭で覚えるのは現実的ではありません。また覚えたところで、コマンドラインのヒストリーに残ってしまうのは好ましくありませんよね。そこでGPGとGitで情報を安全に保管できるコマンドラインベースのパスワードストア「pass」を使います[2]⁠。パスワードストア内にGPGで暗号化した状態でシークレットを保存し、利用時はそれをコマンド内で復号してoathtoolに渡すわけです。まずは「pass」パッケージをインストールしてください。

passパッケージのインストール
$ sudo apt install pass

passコマンドに「init」サブコマンドを指定して実行してください。引数には、暗号化に使うGPGの鍵IDを指定してください[3]⁠。これでパスワードストアが初期化されます。

パスワードストアを初期化する
$ pass init (GPG鍵ID)

passの使い方を簡単に説明します。まずストアにパスワード情報を追加するには「pass insert」コマンドを使います。引数にはパスワードの名前を指定します。たとえば以下はGoogleアカウントのパスワードを保存する例です。

Googleアカウントのパスワードを保存する例
$ pass insert google/password
mkdir: ディレクトリ '/home/h-mizuno/.password-store/google' を作成しました
Enter password for google/password: 
Retype password for google/password: 

保存したパスワードを読み出すには、サブコマンドなしでpassコマンドを実行します。また「-c」オプションをつけると、標準出力ではなくクリップボードにパスワードがコピーされます。

上記で保存したパスワードを読み出す例
$ pass google/password

passでワンタイムパスワードを扱うには、pass-otpというエクステンションを追加でインストールする必要があります[4]⁠。Ubuntuにはまだパッケージがないため、以下のコマンドでソースからインストールしてください。

pass-otpエクステンションのインストール
$ git clone https://github.com/tadfisher/pass-otp
$ cd pass-otp
$ sudo make install

pass-otpが扱うシークレットのフォーマットは、以下のように「otpauth://」からはじまるURI方式です。二要素認証を設定する画面で提示されるQRコードを読み込むと、このフォーマットのURIが取り出せるようになっています。

URI形式のシークレット
otpauth://totp/totp-secret?secret=AAAAAAAAAAAAAAAA&issuer=totp-secret

先ほど作成したGoogleアカウントのパスワードを保存したファイルに、シークレットを追記しましょう。追記には「pass otp append」コマンドを使います。追記後はpassコマンドで中身を見て、2行目にURIが正しく記載されていることを確認しておくとよいでしょう[5]⁠。

URIを既存のパスワードファイルに追記する。パスワード同様、入力した文字はエコーバックされないため、コピ&ペーストを推奨
$ pass otp append google/password
Enter otpauth:// URI for :
Retype otpauth:// URI for :

「pass otp」コマンドをサブコマンドなしで実行すると、ワンタイムパスワードを生成できます。

ワンタイムパスワードを生成する
$ pass otp google/password
123456

QRコードからシークレットを登録する

URIフォーマットは非常に長いため、キーボードから(2回も)入力するのは面倒ですよね。そもそもサービスから提供されるQRコードからURI文字列を取り出すのも手間です。そこでQRコードから直接シークレットをpassに登録してしまいましょう。

画像ファイルからQRコードを読み出すには「zbar」を使います。⁠zbar-tools」パッケージをインストールしてください。

zbar-toolsパッケージのインストール
$ sudo apt install zbar-tools

zbarimgコマンドの引数にQRコードを含む画像を渡すことで、中身をテキストとして取り出せます。その際「-q」⁠--raw」オプションをつけると、余計な出力を抑制し、URIそのものだけを取り出せるので便利です。

QRコードを含む画像ファイルを読み、テキストとして出力する
$ zbarimg -q --raw (画像ファイル)

先ほどは対話的にURIを入力した「pass otp append」ですが、パイプを繋げた場合は標準入力からURIを流し込むことができます。つまり以下のようにすることで、画像ファイルから直接URIをpassに保存できます。

QRコードから直接URIをパスワードストアに保存する例
$ zbarimg -q --raw (画像ファイル) | pass otp append google/password

だいぶ楽になりましたが、こうなるとQRコードを画像として保存するのが面倒くさいですよね。また画像ファイルの後始末を忘れたら大惨事になるかもしれません。そこでその場でスクリーンショットを取得して、QRコードを読むようなスクリプトを組みましょう。あとはこのスクリプトとpass otpをパイプで繋げば、画面上の指定の範囲内からQRコードを読み取り、パスワードストアに格納できるでしょう。

ImageMagickのimportコマンドを利用して指定範囲のスクリーンショットを取得し、QRコードを読み取った上で一時ファイルを削除するスクリプト(readqr.sh)の例[6]
#!/bin/sh

QR=$(mktemp ~/XXXXX.png)
import ${QR}
zbarimg -q --raw ${QR}
rm -f ${QR}
readqr.shを使い、スクリーンショットの取得からシークレットの格納までを一括で行う例
$ readqr.sh | pass otp append (パスワードファイル名)

いかがでしょうか。普段黒い画面とキーボードで作業をしているのに、Webサービスへのログインの時だけスマホをごそごそするのが面倒くさい。そのような悩みを持っている場合には、ぜひpass-otpとoathtoolを試してみてください。

おすすめ記事

記事・ニュース一覧