TOTPによる多要素認証を導入した話
この記事はHacobu Advent Calendar 2024の16日目の記事です。
はじめに
株式会社Hacobu テクノロジー本部 Platformチームの田代です。
Platformチームは自社プロダクトのインフラ構築・運用から、共通機能開発まで幅広い領域を担当しています。
本記事では今夏に導入した多要素認証の概要についてご紹介したいと思います。
セキュリティ意識の高まり
物流2024年問題を受けて物流業界でも急ピッチでDXが進んでいます。
同時に、近年のセキュリティインシデントの増加・大規模化を考えるとセキュリティ対策も疎かにはできません。
Hacobuが提供しているクラウド物流管理ソリューション『MOVO(ムーボ)』にも多要素認証の実装を求める顧客企業が増えていたため、今回導入の運びとなりました。
多要素認証の一般的な知識
前提知識として、多要素認証についての一般的な内容を紹介しておきます。
- 認証とは、「利用者が本人であるか」という真正性(Authenticity)を確かめること。
- 認証には3つの要素を使う。
- 知識情報(パスワード、秘密の質問 etc.)
- 所持情報(デバイス、電話番号 etc.)
- 生体情報(指紋、顔 etc.)
- 多要素認証(MFA: Multi Factor Authentication)とは、複数の要素を組み合わせて認証を行うこと。その中でも2種類の要素を組み合わせる事が多いため、二要素認証(2FA: 2 Factor Authentication)と呼ばれることも多い。
簡単に言うと、
「パスワード(知識情報)がバレちゃったら第三者に勝手にログインされてしまうところだったけど、登録したスマホを持っているか(所持情報)も確認するようにしたので、簡単には不正ログインできなくなるよ」
といったところでしょうか。
MOVOでの多要素認証
MOVOには既に知識情報(ID/パスワード)による認証の仕組みがあったため、ここに二要素目として所持情報(デバイス)を加えることになりました。
MOVOは主にブラウザ上で動くサービスです。顧客企業の利用環境は様々ですから、生体情報を取得できないケースも多いだろうと判断しました。
所持情報としては以下2つの方法が広く一般に使われています。
- SMS(SMS One Time Password)認証
- サーバから、登録した電話番号にSMS(ショートメッセージ)で6桁程度の認証コードを送信。
- ユーザは受け取った認証コードをログイン時に入力することで認証を行う。
- 「登録した電話番号でSMSを受け取れるデバイスを所持している」ということを検証している。
- TOTP(Time-based One Time Password)認証
- 一般にスマートフォンデバイスにインストールした専用アプリ(例: Google Authenticatorアプリ, Microsoft Authenticatorアプリ)を利用する。
- サービスが表示したQRコードを専用アプリで読み取ることで、サーバ側とデバイス側で認証コードを計算するのに必要な情報を共有することができる。
- ユーザはアプリ上で一定時間(30秒程度)ごとに切り替わる6桁程度の認証コードをログイン時に入力することで認証を行う。
- 「サーバ側と情報を共有したデバイスを所持している」ということを検証している。
MOVOではTOTP認証を実装することにしました。セキュリティの観点ではSMS認証よりもTOTP認証が推奨されているようです。
- SMS認証はSIMスワッピング攻撃に対するリスク、通信経路での傍受リスクが高い。
- また、TOTP認証では認証コードがデバイス上で計算されるため、携帯キャリア、通信速度に依存しないこともメリット。
TOTP認証実装
ここからは実装の話に入ります。
細かい部分を除けば、主に以下2つのフローを実装することになります。
- デバイス登録フロー
- 認証フロー
デバイス登録フロー
デバイス登録フローでは、TOTPによる多要素認証を始めるためにデバイスの登録を行います。基本的にはユーザは最初に一度だけ、このフローを通ります。(デバイスを変更・追加したい場合は再度行う必要があります。)
デバイス登録の目的は、Authenticatorアプリとサーバ間で秘密鍵を共有することです。TOTP認証では現在時刻と秘密鍵から生成するハッシュを使って6桁程度の認証コードを計算します。詳細な実装についてはRFC6238や、それを解説するブログを読むと理解が進むと思います。
以下にデバイス登録フローの簡単なシーケンス図を記載します。
デバイス登録リクエストを受けてサーバ側はすぐに秘密鍵を生成していますが、秘密鍵を永続化するのはユーザから認証コードを送り返してもらったあとにしています。Authenticatorアプリ側にも秘密鍵が登録されたということを確認したいからです。
Authenticatorアプリに読み込ませるQRコードは以下のようなURLフォーマットに従っている必要があります。
otpauth://{Type}/{Label-issuer}:{Label-accountname}?secret={Base32エンコードされた秘密鍵}&issuer={Issuer名}
実際にはこのようなURLになります。
otpauth://totp/Example:user@example.com?secret=JBSWY3DPEHPK3PXP&issuer=Example
このURLをQRコードにしてみました。このQRコードは実際にAuthenticatorアプリで読み取ることができます。
Microsoft Authenticatorアプリで読み取った結果、アプリ画面には6桁の認証コードが表示されます。画面表示中の”Example”は{Issuer}の値をそのまま表示しているようです。{Label-issuer}のほうは利用されませんでした。
Google Authenticatorが公開している仕様では、{Issuer}と{Label-issuer}の両方が定義されているときは{Issuer}の値を優先するとのことです。古いバージョンでは{Label-issuer}の値を使っていたらしく、歴史的経緯のために両方残されているようですが、両方同じ値を指定しておくことを強く推奨しているようです。
Google Authenticator, Microsoft Authenticatorはおおむねこの仕様に則った動きをしているように見えます(筆者調べ)。ただしこの仕様書に記述が無い箇所についてはそれぞれ差異がありますし、同じアプリでもiOSとAndroidでの実装で異なる箇所があります。
ちなみにアイコンが表示されるのがMicrosoft Authenticatorの特徴です。(Google Authenticatorにはアイコン表示がありません。)
GitHub GithubやAWS, SlackやFacebookのような大手のサービスでは馴染みのあるアイコンが表示されますが、それ以外のサービスではこのような人型のアイコンになります。(iOSの場合。Androidの場合はIssuerの文字列から切り取った2文字になる。)
このアイコンをカスタマイズする方法はわかりませんでした。要望はあるようなのですが、対応されていないみたいです。大手サービスだけは何かしらの取り扱いがあるのかもしれません。
認証フロー
認証フローは名前の通り、認証を行います。MOVOではID/パスワード認証のあとに、TOTPによる認証フローが始まります。
前述のデバイス登録フローの後半部分とほとんど同じシーケンス図となっています。デバイス登録フローを実装していれば大部分のコードは共通化できるでしょう。
その他気をつけるべき諸々
- リプレイアタック対策
- TOTP認証の仕様では30秒間(設定次第ではより長期間)同じ認証コードを正当なものとみなします。
- つまり30秒以内であれば、正当なユーザがログインした後であっても、不正なユーザが傍受した同じ認証コードを使ってログインを成功させてしまいます。(いわゆるリプレイアタック)
- これを防ぐために、一度使用された認証コードを一定期間保持するなどして、2度目以降は拒否するなどの対策が必要です。
- 通常のID/パスワード認証と、多要素認証の間隔に制限を設ける。
- セキュアにするなら短いほうが良いでしょう。ただし短くしすぎるとユーザのログイン体験を損なってしまうかもしれません。
- ID/パスワード認証成功時のtimestampをサーバ側で保持しておく必要があります。
- ログイン失敗回数に制限を設ける。
- TOTP認証で入力されるコードは6桁程度なので、取りうる値の種類が少ないです。
- この機能は通常のID/パスワード認証でも設けるべきですが、TOTP認証ではより重要であると言えそうです。
終わりに
本記事ではHacobuにおける多要素認証導入の経緯と、TOTPベースの多要素認証について紹介しました。
今は使いやすいIDaaSも普及しており、多要素認証を自分で作る機会は減っているかもしれませんが、実装することになった方の参考になれば幸いです。
Discussion