(※開発素人者です。まだまだ勉強中のため、間違いなどありましたらお教えいただけると大変助かります。)
検討の背景
- これまで実装してきた知識認証(パスワード)による認証では、他人がパスワードを推測、総当たりなどでセキュリティリスクがある。その他の安全かつ便利な認証方法が欲しかった。
- 個人アプリのセキュリティ不安が残るため、個人情報をできるだけ扱いたくない。
各認証方法について
パスワード認証
ユーザー名やメールアドレスとパスワードの組み合わせで認証する最も一般的な方法。パスワードはハッシュ化する。
- セキュリティリスク:リスト攻撃やブルートフォース攻撃、パスワード漏洩や使い回しによるアカウント乗っ取りが発生しやすいため、機密情報系(個人情報だけでなく個人間チャットやカレンダーなど含む)には不安。今回の検討の発端となった方法。
- デメリット:パスワード漏洩による他人のなりすましの脆弱性。
二要素認証(2FA)
ワンタイムパスワード
主に SMS で、特定端末に一回限りの乱数を送り、ユーザー側で入力する。
- セキュリティリスク:アプリだけでなく、SMS のセキュリティにも依存。OTP の盗聴や中間者攻撃が生じる可能性がある。近年だと、SIM スワップのリスクあり。
- デメリット:SMS を受信できる端末が必要。また、SMS を送るために、電話番号をユーザーから収集する必要がある。
マジックリンク
主にメールで、一回限りの短時間トークンを含む URL リンクをユーザーに送り、そのリンクをクリックすることで、認証とみなす。
- セキュリティリスク:アプリだけでなく、メールのセキュリティにも依存。盗聴や中間者攻撃が生じる可能性がある。
- デメリット:ユーザーのメールアドレスを収集する必要がある。メールのリンクのクリックが常態化することは、フィッシングのリスクが高くなるため、ユーザーのセキュリティリテラシーの観点からはあまり推奨されない。
ソーシャルログイン(OAuth / OpenID Connect)
Google や Facebook などの既存アカウントで認証を行い、新規登録やパスワード管理を省略する方法。
- セキュリティリスク:OAuth 認可コードの盗聴や偽装、トークンの漏洩。依存先サービスの障害がログインに影響する。
- デメリット:ユーザーの SNS アカウントが利用できる端末が必要。
シングルサインオン(SSO)、専用認証アプリ系?
一度の認証で複数のサービスにアクセス可能にする認証方式(例:SAML 認証、代理認証)。
- セキュリティリスク:一度の認証情報漏洩で複数サービスが危険にさらされるため、初回認証の強化が重要。
- デメリット:場合により、専用認証アプリなどが別に必要。場合により、ユーザーの SNS アカウントが利用できる端末が必要。
生体認証
指紋、顔認証、虹彩認証など本人の生体情報を利用した認証方式。
- セキュリティリスク:生体情報自体の漏洩が永久的なリスクとなる可能性がある。ケガ等による認証不可リスクもある。
- デメリット:生体認証を扱える端末が必要。
パスキー(パスワードレス認証の一種)
公開鍵暗号方式を利用し、秘密鍵はユーザーデバイスで保護。生体認証・PIN などを用いて秘密鍵の使用を制限。フィッシング耐性が高く、パスワード漏洩リスクなし。マルチデバイス対応可能。
- セキュリティリスク:デバイス紛失・乗っ取り時のリスク(秘密鍵の悪用)、デバイス内セキュリティに依存、実装の複雑さに伴うミスリスク
- パスキーを扱える端末が必要。
※以下は初回の認証ではなく、主にログインの継続性を担保するための認証方法(だと思う)
セッション認証
サーバー側でセッション ID を生成し、ブラウザのクッキーに保存してユーザーの認証状態を管理する。
セキュリティリスク:セッション ID の漏洩によりセッションハイジャックや不正アクセスが可能になる。CSRF 攻撃にも注意が必要。セッション固定攻撃やCookie設定ミスによるリスク。
トークン認証(JWT など)
ログイン時にアクセストークンを発行し、API リクエスト時にトークンを送付して認証する。
セキュリティリスク:トークンの盗難や長期間の有効期限に注意。保存場所の安全性も重要(Cookie やローカルストレージ等)。XSSによるCookie・セッションIDの盗難を防ぐため、認証cookieはlocalstorage より http only/secure cookie に管理したほうがセキュリティ上安全。Secure属性と同時にSameSite属性も必ず指定する。アクセストークンの有効期限設計とリフレッシュトークン漏洩。
上記のまとめ表
認証方式 | 説明 | セキュリティリスク | デメリット |
---|---|---|---|
パスワード認証 | ユーザー名やメールアドレス+パスワード/ハッシュ化 | リスト攻撃、総当たり攻撃、漏洩・使い回しによる乗っ取り | パスワード漏洩によるなりすまし脆弱性 |
2FA(OTP/SMS) | SMS で一回限りの乱数送付・入力 | OTP 盗聴、中間者攻撃、SIM スワップ | SMS 端末必要・電話番号収集が必要 |
2FA(マジックリンク) | メールで短時間トークン URL 送信・リンククリック | メール盗聴、中間者攻撃、フィッシング | メールアドレス収集・リンク常態化でフィッシングリスク増 |
ソーシャルログイン | Google, Facebook 等アカウント利用 | OAuth 盗聴・偽装、トークン漏洩、依存先障害 | SNS アカウント利用端末必要 |
SSO(シングルサインオン) | SAML 等による一度の認証で複数サービスログイン | 認証漏洩で全サービス危険、初回認証に注意 | 専用認証アプリ・SNS 端末が必要な場合あり |
生体認証 | 指紋・顔・虹彩など生体情報で認証 | 生体情報漏洩、認証不可リスク(ケガなど) | 生体認証端末が必要、生体情報管理の懸念 |
パスキー | 公開鍵暗号方式・パスワードレス・マルチデバイス | 秘密鍵紛失・乗っ取り、デバイス依存、複雑な実装ミス | パスキー対応端末が必要 |
セッション認証 | サーバーでセッション ID 生成しクッキーで管理 | セッション ID 漏洩、ハイジャック、CSRF 攻撃 | サーバ管理負荷、クッキー依存 |
トークン認証(JWT 等) | アクセストークン発行し API 認証 | トークン盗難、長期有効期限、保存場所(Cookie/LocalStorage) | HttpOnly/Secure Cookie 推奨、LocalStorage は XSS リスク |
今回検討した流れ
-
セキュリティの高いといわれるパスキーを使用
- パスキーは、一つのユーザーに複数端末を登録できるのが通常仕様。(端末紛失リスクがあるため?)
- 一方で、他人が既存ユーザーに勝手に別端末を追加登録できてしまう。
- つまるところ、パスキー認証自体は認証となるが、他の端末追加を防ぐのは別の実装が必要と判明。
-
パスキー + メールへのマジックリンクに更新
- 新規登録時に、メールアドレスを入力させる方式に修正。
- 端末追加時には、メールをユーザーに送り、承認させる。
- ただし、ログインのたびにメール承認させるのは不便なため、一度パスキーを登録してしまえば、パスキーでログインできることとした。
- ユーザーのメールアドレスは収集したくなかったが、SMS 電話番号よりも漏洩した時のリスクは低いと判断。
- 一方、マジックリンクはクリックのみで承認されることになるが、他人が偽装メールをユーザーに送り、ユーザーの盲目クリックをさせるリスクがあると考えられた。
-
(今ココ) パスキー + メールへのマジックリンク + マジックリンク後に、passcode(OTP)を表示させ、追加端末側で、その passcode を入力しないと完了しないように更新
- 承認側に passcode を表示させて、追加端末側で入力させることで、承認側 A のマジックリンク承認と、追加端末側 B の passcode 一致がそろわないと、完了できない仕様とした。
- マジックリンクメール本文に passcode を追記してしまう方法も可能だが、メールだけで全て完結させるより第三者が passcode を盗む難易度が上がるため、この実装とした。(passcode はメールではなく、別画面での入力を求めることで盗難リスクを低減した。)
思ったより、複雑になってしまった気がします💦
「最後は結局メールかい!」というのも、ごもっともな指摘です。
「個人情報をできるだけ扱いたくない」+「(一方で)マルチデバイスを実現したい」という欲張りを目標にしたら、この形になりました。
言い訳としては、一応初回以降のログインは、上記の仕組みで端末のパスキーのみを使ってログインできるようになります。
もっと良い方法がありましたら、教えてください!