自社サービスにスマホアプリによる2段階認証を導入する

# 目的
* 開発者が自社サービスに導入するときに知っておきたい情報がまとまってて欲しい
* 非エンジニアだけどサービス画面などを設計する人に説明するときの図が欲しい

# 概要
ユーザーにスマホアプリをインストールしてもらって、アプリが発行したトークンをワンタイムパスワード(以下OTP)として使用する
 


# フロー
![ユーザー登録](http://www.plantuml.com/plantuml/png/SoWkIImgAStDuIh9BCb9LNZSlEnnyvx7JTk0vFEPkr_sR7esiuahCRaalqWXj3YrY0j4AEZQAOId9WMd5fLb9gTQOT74XP16L_oqTUEpEPqlUxS-RfhqVChyPnqJY5JyT7xtuwR65_k05ar-tBWWCEXnqsQGjSsTJ_REmJG6v50h1IHLBnQkUBva5rT-GOd2uwP5ZvkdFsvUzMpEgiTDInutRtxSjFDnSmTOuUh7ZUkUBjZoT487bu82pBiGqw1AcjO3TK3S3ZS9w41CaxXIyr8KkD3CWEjeN8xikbLhWLxZSW3g0CO60000)
[編集](http://www.plantuml.com/plantuml/uml/SoWkIImgAStDuIh9BCb9LNZSlEnnyvx7JTk0vFEPkr_sR7esiuahCRaalqWXj3YrY0j4AEZQAOId9WMd5fLb9gTQOT74XP16L_oqTUEpEPqlUxS-RfhqVChyPnqJY5JyT7xtuwR65_k05ar-tBWWCEXnqsQGjSsTJ_REmJG6v50h1IHLBnQkUBva5rT-GOd2uwP5ZvkdFsvUzMpEgiTDInutRtxSjFDnSmTOuUh7ZUkUBjZoT487bu82pBiGqw1AcjO3TK3S3ZS9w41CaxXIyr8KkD3CWEjeN8xikbLhWLxZSW3g0CO60000)
これでWebサーバーとユーザーのアプリで鍵(shared secret)が共有されます。

![ログイン](http://www.plantuml.com/plantuml/png/SoWkIImgAStDuIh9BCb9LNZSlEnnyvx7JTj0PFFQnqqR7ZSjUToy-TdC9gx4v9By8eNGujGYBX2Xe6kd49wQ59nQL9QPd6gbWAoS2o61hknfwyPdSpfVzcvzt3JfQMlpuyRLnWRFbqzwt3ZbSUCA8E_faiwdFTCUDyvytDJzkBa3x9hrG2E1ofzFcV-iOmBOSg2X80feQzEYnytJ7pUlUjoud5f6e_eAHTtJrgLFksRZyHwA-iPrZviR7pSjVzpS31JXIimfKV3tvb9GGDKEzGXIy3tTrmFK-tpAxfTp5t6bvXIdAlYvuzI6iEzfM--BPOtFDZR1TEQbS3a02M3q0W00)
[編集](http://www.plantuml.com/plantuml/uml/SoWkIImgAStDuIh9BCb9LNZSlEnnyvx7JTj0PFFQnqqR7ZSjUToy-TdC9gx4v9By8eNGujGYBX2Xe6kd49wQ59nQL9QPd6gbWAoS2o61hknfwyPdSpfVzcvzt3JfQMlpuyRLnWRFbqzwt3ZbSUCA8E_faiwdFTCUDyvytDJzkBa3x9hrG2E1ofzFcV-iOmBOSg2X80feQzEYnytJ7pUlUjoud5f6e_eAHTtJrgLFksRZyHwA-iPrZviR7pSjVzpS31JXIimfKV3tvb9GGDKEzGXIy3tTrmFK-tpAxfTp5t6bvXIdAlYvuzI6iEzfM--BPOtFDZR1TEQbS3a02M3q0W00)

生成されるトークンは30秒毎に変化する（時間は変更可能）。逆に言うと30秒間は何度計算しても同じトークンが生成される。サーバーとクライアントで同一時間帯に同一ロジックでトークンを生成することで同じトークンが生成されるので、これをOTPとして利用する。30秒毎の切り替えのタイミングで生成してしまうと認証に失敗するので再入力を簡単に行えるような画面設計がいいかも(Appleのデバイス認証みたいに各桁独立の入力ボックスで１文字入力ごとにフォーカス遷移、エラー時はメッセージを表示・フォームクリア・１桁目にフォーカス、など）

# 本方式の補足説明
* 時刻で制限する方式をTOTPという([RFC6238](https://tools.ietf.org/html/rfc6238))
* サーバー、クライアント共にNTPで時刻同期してある前提
* 共有鍵とログインパスワードの両方が漏洩した場合は攻撃者がOTPを生成してログインすることができる
* 共有鍵はログインしている本人は何度でも再生成可能で、再生成時は過去の鍵を無効にする(TOTPの仕様ではなく決めの問題だが、端末を紛失・貸与したなどで漏洩の可能性がある時にスムーズに過去の鍵を無効にできるべき
* ユーザーが共有鍵を紛失してログイン不可となった場合、再ログイン可能にさせるためにはきちんと本人確認を行う必要がある(ログインパスワードを入手した攻撃者による偽の紛失報告を防がなければならない

# クライアントアプリ
* クライアントのトークン生成アプリでメジャーなのはGoogle Authenticator([iOS](https://itunes.apple.com/jp/app/google-authenticator/id388497605)/[Android](https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2&hl=ja))、[Authy](https://authy.com/)、[Duo Mobile](https://duo.com/product/trusted-users/two-factor-authentication/duo-mobile)など
* ユーザーが最初のQRコードを一つの端末でのみ読込を行い、その端末を紛失した場合は共有鍵の紛失となる
* AuthyにはCloud backup機能があり、ユーザーがオプトインで設定することにより別の端末から共有鍵を取り出すことができる
* Authyはクラウドに保存する前に暗号化しているし、別のデバイスで取り出すときはSMS認証などを行うようだが、Authyサーバーの情報漏洩リスクは増えるのでユーザーに選択してもらう際にはトレードオフの説明があった方がよさそう（参考：[公式blog](https://authy.com/blog/how-the-authy-two-factor-backups-work))

# もっと基本的な情報
## 2段階認証とは
ログインパスワードとは別になどをユーザーに送付して、本人以外がなりすましログインしにくくする仕組み

## その他の2段階認証
* OTPをメールで送信という手もあるが、盗聴の可能性が高くなる
* これまでは電話番号認証を兼ねてケータイのSMSにOTPを送付する方式が多かった、ただし送信料がかかる
* 利用者にある程度のネットリテラシーを前提としたサービス（スマホ保有者、またはPCでエミュレータ動かせる等)であればOKなので最近この方式が増えている
* 時間制限のないHOTP([RFC4226](https://tools.ietf.org/html/rfc4226)）というのもあり、TOTPはこれをベースにしている

# ポエム
* 「2段階認証 導入」とかでググってたら単にgoogleアカウントとかを2段階認証にする手順みたいのが技術記事としてあがっててびっくりした
* 2段階認証でトークンを生成するためのソフトウェアや解説記事は結構あるけど、トークンアプリにQRコード読み込ませた時の動作などについての解説があまり見つからなかった（あったけどわかりにくかった