ユーザー認証の緊急事態に備えて提供しておきたい、セッション管理とセキュリティイベントログについて

ritou です。

## 概要

以前、 WebAuthn の流れで、アカウントリカバリーについて書きました。

[WebAuthn など新しい認証方式を受け入れる際の「アカウントリカバリー」の考え方](https://qiita.com/ritou/items/20679287600b61b124fc)

上記の投稿は

* 複数の認証方式
* ユーザー自らの設定変更機能

を用意することで「詰む」ことを避けようという、いわゆる「クレデンシャルマネージメント」の話でした。

例えば家の中で FIDO2 対応のセキュリティキーを失くしたような場合は悪用されることをあまり想像する必要はないかもしれませんが、会社や移動中に失くした、もしくは盗まれた可能性があるような場合は話が別でしょう。

『ログイン方法としてそのセキュリティキーに紐付けられた「公開鍵」を無効化できれば安心』とはいかず、そのセキュリティキーを用いた第３者からのログインがなかったかどうか、そのセキュリティキーでログイン可能なサービスを使い続けられていないかが気になると思います。

ということで、今回は

* セキュリティイベントログ : 認証や設定変更などの履歴
* ログインセッション管理 : ログイン状態になっている(可能性がある)ブラウザやデバイスの管理

のあたりも大事だよねっていう話を書きます。

## セキュリティイベントログ

### 概要

* 認証、再認証(パスワード確認など)が成功(失敗)した時
* (実装によっては)ログアウトした時
* クレデンシャル(相当の情報)の変更 : パスワードやメールアドレス、SMS番号の設定、変更
* 外部サービスへのアクセス許可 : ID連携機能を提供しているサービスではアクセスを許可したタイミングなど

あたりのログを保存しておき、ユーザーから閲覧可能にしておくことで、ユーザーが「自分のアカウントで何が行われたのか」を確認できます。

この機能は、いくつかのサービスで提供されています。

ヤフーの「ログイン履歴」では、接続元のホスト名、IPアドレスなどの環境、ログイン方法が確認できます。
ちなみにこれは私が(略

[Yahoo! JAPAN IDに関するヘルプ&nbsp;-&nbsp;ログイン履歴とは](https://www.yahoo-help.jp/app/answers/detail/p/544/a_id/42002#info)

> 「ログイン履歴」とは、お客様がYahoo! JAPANにログインした日時やサービス名等を表示する機能です。ログインに成功した履歴が残り、第三者による不正な操作がないかを確認できます。

Googleの「最近のセキュリティイベント」機能では、認証まわりの設定変更がいつ行われたのかが確認できます。

[セキュリティ関連の通知やアラートの表示 - Google アカウント ヘルプ](https://support.google.com/accounts/answer/6076895)

> [最近のセキュリティ イベント] ページでは、セキュリティに関連する過去 28 日間のアクティビティを確認できます。たとえば、パスワードの変更やアカウントへの電話番号の追加といったアクティビティが表示されます。
> この情報を使用して不審なアクティビティがないか確認することで、アカウントを安全な状態に保つことができます。 

なつかしのmixiでもアカウントアクティビティ機能として提供しています。
ちなみにこれも私が(略

[[mixi] ヘルプ &gt; ログイン &gt; アカウントアクティビティとは](https://mixi.jp/help.pl?mode=item&item=1193)

> アカウントアクティビティとは、あなたのログインやアカウント設定に関わる行動履歴のことです。
> アカウントアクティビティを見ることで、ログイン・パスワードの変更などを、いつ、どこから行ったのかを確認できます。

githubにも "Security History" ってのがありますね。

[Reviewing your security log - GitHub Help](https://help.github.com/en/articles/reviewing-your-security-log)

> You can review your account's security log to better understand the actions you've performed in the last 90 days.

クレデンシャルの変更時にEメールで通知すると言うのは一般的かと思いますし、いつもと異なる環境や初めて利用する環境からのログイン時に通知をするサービスも増えてきました。しかし、メールなどは正しく届かない可能性もありますし、消してしまって後から確認することが困難な場合もあるでしょう。

また、内部では既にこのようなセキュリティイベントのログを残しておいて、CSの問い合わせ対応などに利用されているところもあるかと思います。

悪意ある第三者の行いがあったかどうかをユーザー自身が "後からまとめて" 確認できるように、このようなセキュリティイベントのログを保存、画面からユーザーに閲覧させるところまで実装してみるのはいかがでしょうか。

### ID連携とセキュリティイベント

ソーシャルログインで Identity Provider(以下、IdP) のアカウントを外部のサービスで利用している場合、 IdP 側でアカウントの乗っ取りなどがあるとそのサービスにも影響を受ける可能性がありますが、外部サービス側で乗っ取られたセッションで利用されたものなのかどうかを判断することは困難でしょう。

Google では、Cross Account Protection という、セキュリティイベントを Google から連携している外部サービスに提供する仕組みを実装しています。

[Google rolls out Password Checkup and Cross Account Protection](https://blog.google/technology/safety-security/google-password-checkup-cross-account-protection/)

> Cross Account Protection helps address this challenge. When apps and sites have implemented it, we’re able to send information about security events—like an account hijacking, for instance—to them so they can protect you, too.

この仕組み、Google が全部独自に考えたものではなく、OpenID Foundation の WG である RISC の仕様に沿って実装されているとあります。

[Google Developers Blog: Working together to improve user security](https://developers.googleblog.com/2019/02/working-together-to-improve-user.html)

> CAP is built on several newly created Internet Standards, Risk and Incident Sharing and Coordination (RISC) and Security Events, that we developed with the community at the OpenID Foundation and IETF. This means that you should only have to build one implementation to be able to receive signals from multiple identity providers.

[Protect user accounts with Cross-Account Protection &nbsp;|&nbsp; Google Identity Platform](https://developers.google.com/identity/risc)

> Cross-Account Protection is based on the RISC standard, developed at the OpenID Foundation.

セキュリティイベントの表現方法については、仕様がドラフトの時に記事を書いたことがあります。

[現在仕様策定中のSecurity Event Token (SET) とは？](https://qiita.com/ritou/items/ca07761fc3a36039be54)

もはやおなじみJWTの形式でセキュリティイベントが表現され、 Google の CAP では外部サービス側がそれを受診するエンドポイントを用意すると言うことでセキュリティイベントの共有が実現できそうです。

このような仕組みを有効に利用してユーザーを保護するために、サービス側もセキュリティイベントの扱いについて考える良いタイミングなのではないでしょうか。

## ログインセッションの管理

### 概要

これはサービスの実装に大きく依存する話だと思いますが、上記のセキュリティイベントのログだけでは、悪い人が現在もサービスを利用しているかどうかがわからない可能性もあります。

アカウントの乗っ取りにも色々あるとは思いますが、

* パスワードもそのまま使われている
* メールアドレスやSMS番号が変更されていない

などの場合、被害者がログインしてパスワードを変更するのが一般的でしょう。
そして、その後にログイン状態である可能性のある第３者を追い出したいと言う気持ちになると思います。

よって、ユーザーによるセッション管理、具体的には

1. 既存のセッションを落とせる
2. 個々のセッションを確認、落とせる

と言う機能が必要になると考えます。とはいえ、そもそもログイン状態のセッションの維持の方法からして各サービスの実装はバラバラでなかなか一般化して実装案を出すのが難しそう、なのでここではふわっとした話に留めておきます。

セッションを落とすところでは

* 「ログアウト」したら全てのセッションを落とすように設計
* 「パスワード変更」などのクリティカルな設定変更を行った時に全てのセッションを落とす

といったあたり、セッションの確認では

* ログイン状態のブラウザ
* アプリの場合はデバイス

とログイン時の環境、最終利用日時などを合わせて表示することでユーザーが確認しやすくなりそうです。

* Google : [最近使用したデバイス](https://myaccount.google.com/device-activity)
* facebook : [セキュリティとログイン](https://www.facebook.com/settings?tab=security&section=sessions&view)
* github : [Sessions](https://github.com/settings/sessions)

いろんな言語のいろんなフレームワークで実装されている Web サービスでセッション確認機能まで必須！と言い切るとなかなか辛いかもしれませんが、セッション破棄の仕組みと組み合わせることでユーザーがより安心できる機能ではあると思います。
新規事業であるとか、既存のものでもお金が絡んでいるサービスではぜひ導入を検討してみてはいかがでしょうか。

### 一括セッション破棄の実装案

例えばフレームワークで発行されるセッションに対して単純に "user_id=(ユーザーID)" をセットするだけでログイン状態を判定しているような場合、どのようにログインセッションを破棄できるかわからない場合もあるでしょう。

* ユーザー毎に `session_seed` のような値を持っておく
* ログインセッション開始(ユーザーIDのセット時)に `session_seed` の値を一緒に保存
* セッション確認時に `session_seed` の値と突き合わせ
* ログアウトやパスワード変更時に `session_seed` の値を変更

みたいな感じで実現できそうです。以前なら `session_seed` のところはパスワードをごにょごにょしたものでも良かったかもしれませんが、今はいろんな認証方式もあるので、細かいところはよしなに考えてもらえばと思います。

## まとめ

セキュリティイベント、セッション管理の重要性について書きました。
Google の CAP についての紹介、セッション破棄あたりの実装例も紹介しました。
この辺りの機能はサービスの本質とは離れたところにありますが、ユーザー起因のトラブルやデータ漏洩などの万が一の状態において、ユーザーが自ら安全を確認できる手段としてとても重要だと思います。

サービスの特性など意識しない一般化したお話として、多数の開発者が意識できるような状態になることを望みます。

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">FIDOが普及する今のタイミングだからというわけではないんだけど、既存セッションの破棄など含めての管理、セキュリティイベントの扱いみたいなとこについても話す機会があれば良いなと思っている。<br><br>意識高いサービスだけユーザーに見せたり機能提供してる状態で他は暗黙の実装状態なのはよくない。</p>&mdash; 👹秋田の猫🐱 (@ritou) <a href="https://twitter.com/ritou/status/1106050230145802241?ref_src=twsrc%5Etfw">2019年3月14日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

セキュリティ関連は何かと物騒ですが、こーゆーの勉強会なら何も萎縮しなくて良いですね。

以上です。
ではまた。