uzuki の投稿一覧

uzuki

開発グループの卯月です。

最近(といっても半年程前ですが)、私が担当しているプロダクトに TOTP で2要素認証を導入しました。
TOTP を扱うライブラリや技術的な資料は複数存在しますが、TOTP に対応した2段階認証アプリの仕様にあわせた otpauth URI 生成方法について、イメージが掴みやすい HowTo 記事が無かったので、その辺りを書いていきたいと思います。

なお、TOTP 認証の実装方法や技術的な解説は本記事では紹介しませんのでご了承ください。

※本記事は2017年6月8日時点での情報を元に作成しています。

どの2段階認証アプリをサポートするか

利用者は基本的にスマホの2段階認証アプリを利用するかと思います。
2段階認証アプリは無料アプリとして無数に存在しているので、内製するよりかは既存アプリを利用した方が良いです。

本記事では、下記2つのアプリで利用可能な otpauth URI のフォーマットについて紹介します。

そもそも otpauth URI って何?

TOTP の設定を2段階認証アプリ側に読み込ませるための URI です。
サーバ側で otpauth URI を生成し、QR コードで表示→スマホで読み込ませ2段階認証アプリに保存する流れになるかと思います。
なお、この otpauth URI には秘密鍵が含まれますので、傍受の危険性のある経路での送信や不必要に保存させることはやめましょう。(例:非暗号化経路でのメール送信等)
QRコードで読み込ませる場合であれば比較的傍受され難いですし、キャッシュは除き2段階認証アプリ以外には保存されないのでお勧めです。

詳しい仕様は下記をご覧ください。

otpauth URI はどう生成すれば良いの?

結論から言うと下記フォーマットに準拠していれば、Google Authenticator / IIJ SmartKey をサポートできます。
otpauth://totp/[issuer]:[accountname]?secret=[secret]&issuer=[issuer]&algorithm=SHA1&digits=6&period=30

では何故このフォーマットで Google Authenticator / IIJ SmartKey がサポートできるのかを説明します。

ベースとなる URI は Google Authenticator / IIJ SmartKey 共通です。
otpauth://[TYPE]/[LABEL]?[PARAMETERS]

TYPE

Google Authenticator では hotp / totp どちらとも対応しています。
IIJ SmartKey では totp のみ対応しています。

今回は TOTP 認証を行いたいので、[TYPE] には「totp」を指定します。

LABEL

Google Authenticator では、accountname のみ または issuer と accountname をコロン(:)で連結したものが利用できます。
IIJ SmartKey では、issuer と accountname をコロン(:)で連結したものが利用できます。

IIJ SmartKey の方が制限きついので、[LABEL] には「[issuer]:[accountname]」を指定します。

issuer(発行者)
Google Authenticator / IIJ SmartKey 共に、値は URL エンコード (ABNF) する必要があります。
基本的にはサービス名にすると良いでしょう。

accountname(アカウント名)
Google Authenticator / IIJ SmartKey 共に、値は URL エンコード (ABNF) する必要があります。
基本的には利用者のアカウント名やログイン ID にすると良いでしょう。

PARAMETERS

secret(秘密鍵)
Google Authenticator / IIJ SmartKey 共に指定必須であり、値は秘密鍵を Base32 エンコードした文字列で指定する必要があります。

これは共通仕様なので、そのまま指定します。

issuer(発行者)
Google Authenticator / IIJ SmartKey 共に、値は LABEL で指定した issuer を指定する必要があります。
Google Authenticator では強く推奨しているだけですが、IIJ SmartKey では指定必須です。

IIJ SmartKey で指定必須なので、必ず指定するようにします。
なお、[LABEL] 内の issuer と同じ値を指定するよう気を付けましょう。

algorithm(ハッシュアルゴリズム)
Google Authenticator / IIJ SmartKey 共に指定は任意(デフォルト値は SHA1)です。
Google Authenticator では、値に SHA1 / SHA256 / SHA512 が指定できますが無視されます。
IIJ SmartKey では、値に SHA1 / SHA256 / SHA512 / MD5 が指定できます。

Google Authenticator では指定しても無視されデフォルト値である「SHA1」が利用されます。
今後デフォルト値が変更となる可能性もなくはないので、念のため「SHA1」を指定しておきましょう。

digits(表示されるトークンの表示桁数)
Google Authenticator / IIJ SmartKey 共に指定は任意(デフォルト値は 6)です。
Google Authenticator では、値に 6 / 8 が指定できますが無視されます。
IIJ SmartKey では、値に 6 / 8 が指定できます。

Google Authenticator では指定しても無視されデフォルト値である「6」が利用されます。
今後デフォルト値が変更となる可能性もなくはないので、念のため「6」を指定しておきましょう。

period(更新間隔(秒))
Google Authenticator / IIJ SmartKey 共に指定は任意(デフォルト値は 30)です。
Google Authenticator では、値に数値が指定できますが無視されます。
IIJ SmartKey では、値に数値が指定できます。

Google Authenticator では指定しても無視されデフォルト値である「30」が利用されます。
今後デフォルト値が変更となる可能性もなくはないので、念のため「30」を指定しておきましょう。

counter
Google Authenticator の TYPE が hotp の時に指定するパラメータなので今回は指定しません。

icon
IIJ SmartKey のオプションパラメータなので今回は指定しません。

まとめ

どのアプリも otpauth URI の仕様は大枠では共通ですが、細かい箇所が結構異なっていますので、サポートしたいアプリの仕様はちゃんと確認することをお勧めします。

再掲しますが、Google Authenticator / IIJ SmartKey は下記フォーマットでサポートできます!
otpauth://totp/[issuer]:[accountname]?secret=[secret]&issuer=[issuer]&algorithm=SHA1&digits=6&period=30

Tags: , ,

uzuki

初めまして。セールスフォースグループの uzuki です。

他のメンバーは Linux や PHP のことについてよく書いていますので、趣向を変えて Windows のことについて書きたいと思います。

Windows には Linux と同じく CUI で操作を行うことが可能な「コマンドプロンプト」という機能があります。MS-DOS 世代の方ならご存じかと思いますが、要は Linux のシェルとほぼ同じような機能です。詳しくは コマンド プロンプト: よく寄せられる質問 に記載がありますので、ご一読頂ければと思います。

また、Linux のシェルスクリプトと同じく、あらかじめファイルに書いておいた命令を行わせることが可能です。このファイルのことを「バッチファイル」と呼びますが、今回はバッチファイルを作成して、任意のアプリケーションを再起動させる方法について書きたいと思います。

バッチファイル とは

先程記載した通り、バッチファイルとは言わば Windows 版のシェルスクリプトのようなものです。作成方法は単純で、拡張子を「.bat」にするだけでバッチファイルとなります。
Cドライブ配下に「test」フォルダを作成し、その中に「hello.bat」というファイルを作成します。hello.bat の上で右クリック⇒編集をクリックし、以下の内容を記載します。

echo hello!
pause

echo はその名の通り、引数を出力するコマンドです。pause を末尾に入れておかないと、コマンドプロンプトのウィンドウがすぐに閉じてしまうため入れています。

このファイルを実行すると、以下の内容で出力されるかと思います。

C:\test>echo hello!
hello!

C:\test>pause
続行するには何かキーを押してください . . .

実行したコマンドが表示されており、なんだか不格好なので、実行したコマンドを非表示にしたいと思います。先程の hello.bat に 1 行加えるだけで実行したコマンドは非表示になります。

@echo off
echo hello!
pause

実行結果は以下の通りとなります。

hello!
続行するには何かキーを押してください . . .

以上がおおまかなバッチファイルの作成方法となります。

任意のアプリケーションを再起動させる手段(コマンドプロンプト編)

バッチファイルの応用例として、任意のアプリケーションをワンタッチで (強制的に) 再起動する方法を紹介しようと思いますが、まずその前段として、コマンドプロンプトで任意のアプリケーションを再起動する方法からご説明します。

例として iexplore.exe (InternetExplorer) を再起動させてみます。

  1. 実行中の iexplore.exe のプロセスIDを検索・表示する
  2. 実行中のプロセスID (PID) を表示させる tasklist コマンドを利用します。
    tasklist は、オプション指定で任意のアプリケーション名 (tasklist コマンドにおける「イメージ名」) に対応するプロセスのみを表示させることが可能です。

    c:\test>tasklist /fi "IMAGENAME eq iexplore.exe"
    
    イメージ名                     PID セッション名     セッション# メモリ使用量
    ========================= ======== ================ =========== ============
    iexplore.exe                  5688 Console                    1     26,584 K
    iexplore.exe                  7316 Console                    1     55,076 K
    iexplore.exe                  4812 Console                    1     44,200 K
  3. プロセスを終了させる
  4. とある名前のアプリケーションに対応するプロセスを終了させるには taskkill コマンドを利用します。taskkill は、オプション指定を行うことにより、アプリケーション名でプロセスを終了させることができます。

    c:\test>taskkill /im iexplore.exe /f
    成功: プロセス "iexplore.exe" (PID 5688) は強制終了されました。
    成功: プロセス "iexplore.exe" (PID 7316) は強制終了されました。
    成功: プロセス "iexplore.exe" (PID 4812) は強制終了されました。

    /fは強制終了オプションです。

    InternetExplorer は複数タブを開いている状態で終了しようとすると確認ダイアログが出てきますので、強制終了オプションを指定しない場合は OK ボタンを押さない限り終了されません。
    そういった事情のあるアプリケーションの場合、強制終了オプションを指定することにより上記のように確認ダイアログによる問い合わせなしに停止可能です (ただし、強制終了で何かしら実害が生じるようであればこの強制終了オプションは利用しないで下さい)。

  5. アプリケーションを起動する
  6. アプリケーションを起動するには start コマンドを利用します。start コマンドはアプリケーションを起動するコマンドになります。第一引数は実行ファイル名ではなくウィンドウのタイトルになるため、例えば下記のように空白文字列を与えます。

    c:\test>start " " "C:\Program Files\Internet Explorer\iexplore.exe"

ではバッチファイルで実行させる方法を紹介します。

任意のアプリケーションを再起動させる手段(バッチファイル編)

では、上記でコマンドプロンプトで実施した内容をバッチファイルにしてみます。

今回やることはそう難しくないので簡単そうですが、バッチファイル(というかコマンドプロンプト)のクセを知らないとドツボにハマります。私はハマりました。

さて、アプリケーションの再起動の順序としては、

  1. 再起動させたいアプリケーションの名前と場所を変数に代入
  2. 実行中のプロセスから再起動させたいアプリケーション名のリストを取得
  3. リストがあればアプリケーションを終了させる
  4. 再起動させたいアプリケーションを起動する

早速ソースを見てみましょう。

@echo off
rem 再起動させたいアプリケーションの実行ファイル名とそのパスを変数に代入
set APP_NAME=iexplore.exe
set APP_FOLDER=C:\Program Files\Internet Explorer\

rem 対象のアプリケーションが起動しているかどうかを、tasklist コマンドと 
rem for 文の組み合わせで判定。tasklist コマンドの結果のうち、
rem /f オプションにより先頭の文字列 (空白またはタブ等の区切り文字まで) 
rem のみを取得し、%%i に代入
for /f %%i in ('tasklist /fi "IMAGENAME eq %APP_NAME%" 2^>^&1') do (
    rem 先頭の文字列がアプリケーション名と等しければ一つ以上のプロセスが
    rem 実行中であり、アプリケーションが起動していると見なせるため、
    rem taskkill で停止処理を実施
    rem (一致がなければアプリケーションは停止していると見なし何もしない)。
    if /i %%i==%APP_NAME% (
        rem taskkill でアプリケーション名を対象にして複数のプロセスを一度に停止
        taskkill /im %APP_NAME% /f
        rem アプリケーション停止後、breakなんて気の利いた機能はないので 
        rem goto でループから抜ける。
        goto ENDLOOP;
    )
)
:ENDLOOP

rem 再起動させたいアプリケーションを起動
start " " "%APP_FOLDER%%APP_NAME%"

わざわざ tasklist でリストを取得・for で処理する必要があるのか? taskill 後に start すればいいじゃないか! とのご指摘もあるかと思います。こんな周りくどいことをしている理由は、アプリケーションが起動している時のみ taskkill で停止する動きとしたかったからです。

例えば、存在しないアプリケーションを taskkill しようとすると以下のような「エラー(Error)」が生じます。

c:\test>taskkill /im iexplore.ex /f
エラー: プロセス "iexplore.ex" が見つかりませんでした。

一方で tasklist でアプリケーション名を指定し、検索を行った場合、そのアプリケーションが動作していない状況では「情報(Information)」として結果が返ってきます。

c:\test>tasklist /fi "IMAGENAME eq iexplore.ex"
情報: 指定された条件に一致するタスクは実行されていません。

分かっていてエラーを生ずるようなコマンドを実行するよりは、必要な時だけ taskkill を実施するような動きとなった方が筋が良いかなと思い、tasklist でアプリケーションのプロセスの有無を確認した上で taskkill を実施しています。その方法としてあまり for 文は他の言語では利用しないかもしれませんが、コマンドプロンプトで利用できるコマンドと制御構造においては比較的妥当な方法かなと思っています (もちろん他にもっとスマートな方法もあると思いますが)。

なお、バッチファイルのソースを書く上で注意しなければならないのは、for文内のキャレット(^)です。% & | > < ( ) などの特殊文字はパイプや入出力として判断されますので、キャレットでエスケープしなければなりません。少し調べてみると、相当めんどくさい仕様となっているようなので、エスケープには気を付けた方がいいです。ドツボにハマります。詳しくは こちら に解析記事があります。

作成したバッチファイルをデスクトップやタスクバーに置いたり、ランチャーやマウスジェスチャーに登録すると、ダブルクリックでサクッとアプリケーションが再起動できて幸せになれます。

Tags: , ,