現在仕様策定中のSecurity Event Token (SET) とは？

# Security Event Token (SET)

IETF で仕様策定中の Security Event Token (SET) が RFC になる前に予習しておきましょう。

## 概要

### 一言で

「セキュリティ/アイデンティティなイベントを JWT で表現する方法」

### 仕様

[Security Event Token (SET)](https://tools.ietf.org/html/draft-ietf-secevent-token) : (2018/5/8 時点ではDraft.11)
※だいぶ煮詰まってるっぽいので、更新されたらなんとか追従します

### 何を表現するのか

* この仕様ではセキュリティ/アイデンティティに関するイベントの表現を可能にすることを目的とする(が、それ以外の目的への利用も可能)
* `Subject` に直接もしくはその周囲に間接的に発生したイベントを `SET Issuer`　の観点から表現する
* `Subject` はアカウントのような永続化されたものでも、HTTP セッションのような一時的なものでも良い
* エンティティ自体の直接的/暗黙的な状態変化やより高度なセキュリティステートメントが対象。例えば...
  * リソースの CRUD
  * アカウントのリセットやサスペンド
  * 有効期限切れ前のトークンの無効化
  * セッションのログアウト
  * 以前に他のユーザに紐づけられたメールアドレスが新しいユーザに紐づけられた

### どのようにやりとりされるのか

* 発行/送信
  * `Subject` の状態変更はユーザーエージェントやセキュリティサブシステムをトリガーにして発生
  * イベントの発行および送信は、それと非同期かつバックチャンネルで発生
* 受信
  * イベントの受信者(`SET Recipient`)は、検証して解釈し、それに対する独立したアクションをとる、もしくは何もしない(SET = コマンドやリクエストではない)

仕様の目的は、それぞれの目的に応じて解釈可能な構文の定義となります。

### 形式など

* JSON Web Token(JWT) 形式
* プロファイルに応じてJSON Web Signature(JWS) で署名つけたり JSON Web Encryption(JWE) で暗号化したり
* HTTP "など" のプロトコルを利用してやりとりできる

## SETの仕様

### SETのJWTクレーム

まずは JWT の Payload について。

* JWT のトップレベルクレームは "SET envelope" と呼ばれる。プロファイルによりいくつかの値が必須だったり任意だったりする。これらの値は Registry に登録されているか、 RFC7519 に定義されている `Public Claims or Private
Claims` であるべき
* この仕様でプロファイルされて定義されている "SET envelope" は、イベントデータに関する情報をSETに含んで提供し、検証するために使用される
  * ** `events` クレームには、イベントの識別子と `Subject` について表現されたイベント固有のデータが含まれる**
  * ** `events` クレームの値は、少なくとも1つのメンバーを持つ JSON オブジェクトでなければならない**
* `events` クレームの値の JSON オブジェクトの各メンバーは、`name`/`value` のペア
  * イベント識別子の `name` はURI文字列、`value` は `event payload` と呼ばれるJSONオブジェクトであり、プロファイリング仕様で定義されたイベント識別子に関連するクレームが含まれている。これらはJWTクレームとして登録する必要はない。
  * `payload` のないイベントは、空のJSONオブジェクト（`{}`）として表現されなければならない
* **複数のイベント識別子がSETに含まれている場合、それらは `Subject` に発生した同じ状態遷移の複数の状況を表すものであり、同じ `Subject` に関する別個のイベントを集約するために使用されることを意図していない**
  * 複数のイベント識別子を含むSETの解釈はこの仕様の対象外

"SET envelope" の個別のクレームについて

* RFC7519 で定義済み
  * `iss` : SET を発行しているサービスプロバイダ(`SET Issuer`)を識別する文字列。REQUIRED
  * `iat` : SET 発行時刻。REQUIRED
  * `jti` : SET の一意な識別子。特定イベント内でユニークである必要があり、クライアントが SET を受信済みかどうかのトラッキングに利用できる。REQUIRED
  * `aud` : SETの1つもしくは複数の audience 識別子。RECOMMENDED
  * `sub` : `SET Subject` を識別する文字列もしくはURI。 SET では通常、状態変更が発生したエンティティの値。OPTIONAL
  * `exp` : 有効期限。SETは発生したイベントを表現するため、NOT RECOMMENDED
* 新規
  * `events`(Security Events) : 一連のイベントステートメントが含まれる。内容は上述の通り。REQUIRED
  * `txn`(Transaction Identifier) : 関連する複数のJWT(SETに限った定義ではない)を関連づけるための識別子文字列。OPTIONAL
  * `toe`(Time of Event) : イベント発生日時。`iat`などと同じ `NumericDate`形式。この値が省略されている場合、発行者は受信者とイベント発生日時を共有しないことを意味する。OPTIONAL

### SETを表現するtype

* media type : `application/secevent+jwt`
* JWT `typ` クレーム : `secevent+jwt`

これらに出会ったら一発で SET として扱うことができる。

## SETの例

### SCIM Password Reset Event

SCIMでパスリセットが発生した時にSET発行 -> 受信側で有効なセッションを持つユーザーエージェントを無効化みたいなユースケースの "SET envelope"

```
   {
     "iss": "https://scim.example.com",
     "iat": 1458496025,
     "jti": "3d0c3cf797584bd193bd0fb1bd4e7d30",
     "aud": [
       "https://jhub.example.com/Feeds/98d52461fa5bbc879593b7754",
       "https://jhub.example.com/Feeds/5d7604516b1d08641d7676ee7"
     ],
     "sub": "https://scim.example.com/Users/44f6142df96bd6ab61e7521d9",
     "events": {
       "urn:ietf:params:scim:event:passwordReset":
         { "id": "44f6142df96bd6ab61e7521d9"},
       "https://example.com/scim/event/passwordResetExt":
         { "resetAttempts": 5}
     }
   }
```

* `"urn:ietf:params:scim:event:passwordReset"` : パスリセットが発生。対象のユーザーIDが含まれる
* `"https://example.com/scim/event/passwordResetExt"` : 最新のパスリセット数(5)が含まれる

### OpenID Connect Back-Channel Logout Event

OpenID Connect で OP から RP にこのトークンのセッションがログアウトしましたよとバックチャネルで伝える仕様がある。
OP は あらかじめ ID Token に `sid` を含み、ログアウトトークンでその `sid` を指定することでRP側にログアウト対象のセッションを伝えられる。(`sid` が含まれない場合、`sub` でログアウト対象ユーザー)
このログアウトトークン自体を SET として表現する時の "SET envelope"

```
   {
      "iss": "https://server.example.com",
      "sub": "248289761001",
      "aud": "s6BhdRkqt3",
      "iat": 1471566154,
      "jti": "bWJq",
      "sid": "08a5019c-17e1-4977-8f42-65a12843ea02",
      "events": {
        "http://schemas.openid.net/event/backchannel-logout": {}
      }
   }
```

対象は `sub` or `sid` により指定されており、`events` の `http://schemas.openid.net/event/backchannel-logout` では値を含まず、空のJSON オブジェクトとなる。

### OpenID Connect Consent Event

これは、単純な OpenID Connect の認可/同意処理が行われたことを表す SET ではない。
なかなか説明しづらいが、イベントの発行者とSETの発行者が異なるプロファイルにおける "SET envelope" となっている。

```
  {
     "iss": "https://my.med.example.org",
     "iat": 1458496025,
     "jti": "fb4e75b5411e4e19b6c0fe87950f7749",
     "aud": [
       "https://rp.example.com"
     ],
     "events": {
       "https://openid.net/heart/specs/consent.html": {
         "iss": "https://connect.example.com",
         "sub": "248289761001",
         "consentUri": [
           "https://terms.med.example.org/labdisclosure.html#Agree"
         ]
       }
     }
   }
```

* ｀"iss": "https://my.med.example.org"｀ : OpenID Connect の Provider だが、**ユーザーの同意を得る医療機関ではない**
* イベントの `sub` であるユーザー(`248289761001`)は OpenID Connect のシステムを通して認証される
* イベントの `iss` である `"https://connect.example.com"` に対して同意が行われた
* `"https://connect.example.com"` は OpenID Connect の RP
* SET は別のRPである `"https://rp.example.com"` に送られる

### RISC Event

OpenID RISC WGにて仕様策定がされている、アカウント無効化のイベントを表現した "SET envelope"

`events` -> `"http://schemas.openid.net/secevent/risc/event-type/account-disabled"` に対象者(`subject`)、理由(`reason`)、発生日時(`cause-time`)が含まれている。

```
  {
     "iss": "https://idp.example.com/",
     "jti": "756E69717565206964656E746966696572",
     "iat": 1508184845,
     "aud": "636C69656E745F6964",
     "events": {
       "http://schemas.openid.net/secevent/risc/event-type/\
       account-disabled": {
         "subject": {
           "subject_type": "iss-sub",
           "iss": "https://idp.example.com/",
           "sub": "7375626A656374"
         },
         "reason": "hijacking",
         "cause-time": 1508012752
       }
     }
   }
```

## SET プロファイルのための要件

SETを特定のユースケースで使用するために、プロファイルの仕様は以下のものを定義する(SHOULD)

* Syntax
  * Top-Level Claims : どの定義済みなJWTのクレームを使うか
  * Event Payload
* Semantics
* Subject Identification
* Validation

## SET と他の用途のJWTとの混同を避けるために

SET と ID Token などその他のJWTが置き換えられた時、検証時に拒否できるようにする必要がある。

* "SETを表現するtype" で定義されたものを利用してSETのみを受け付ける
* 特定のクレームの有無で判断する
  * `events` クレームの検証は SET として他のJWTを渡されるのを防ぐために効果的

混同を防ぐための一般的な JWT のベストプラクティスは [JSON Web Token Best Current Practices](https://tools.ietf.org/html/draft-ietf-oauth-jwt-bcp) に記載されている。

## Security Considerations

### Confidentiality and Integrity

* Confidentiality
  * 機密情報が含まれる場合はトランスポート層のセキュリティメカニズムを使う(SHOULD) : TLS >= 1.2みたいなやつ
  * セキュリティイベントが3rdパーティーを介したり、個人情報(PII)を含む時は JWE を使う(SHOULD)
* Integrity
  * 他の方法で保証されていない場合は、JWS を使って Issuer により署名されたものを認証/検証する(MUST)

### Delivery

* この仕様では配送方法については定義されていない
* "Confidentiality and Integrity" に加え、配送方法がセキュアかどうか、保証されているかどうかを検討する(MUST)
  * 例えば JWE + TLSなし の場合、正しいエンドポイントがそれを受信して正しく処理されたかの保証がない

### Sequencing

* この仕様では、あるシーケンス中の複数SETの順番の意味について定義していないが、イベントの種類や性質によっては重要かもしれない
* `iat` クレームを用いる場合、注意する必要がある(SHOULD)
  * 分散されたシステムでは時刻ずれがあるかもしれないので保証されない
* 順序が重要になる場合、検出する仕組みを定義する(SHOULD)
  * 例えば `txn` クレームにカウンタを入れるなど。しかし分散されたシステムでは(以下略

### Timing Issues

* オリジナルのアクションに対して、SETが非同期もしくはoobで送られるような場合、SETの到着が前後する可能性を考慮する必要がある
* SETの表現方法とタイミングの問題を考慮する(SHOULD)
  * ログアウトで既存のセッション一括削除をする機能がある時に、その後の再ログインとセッション削除のタイミングが重なるなど
  * ユーザー全体ではなく、`sid` 単位にしたり、ログアウトイベントの後に開始されたセッションは残すとか

## Privacy Considerations

* 監査目的で SET 保持する必要がある場合、署名を用いて正しさを検証する機能を提供できる
* コンテンツがSET受信者の特定のビジネスとプロトコルの対象となるように、SETを特化させる(SHOULD)
* 個人情報を共有する場合、SETの発行者と検証者がユーザーの同意や利用規約で法的な合意を得る(SHOULD)
* 対象者の識別子の伝搬は個人情報として認識されうるので、可能であればそれを防ぐ手法を考案する(SHOULD)
  * salted hashの値を渡すなど
* 場合によっては、SET受信者が異なるイベント情報を相関させることで、SET発行者が共有しようとしなかった情報を得ることもできる。相関関係がプライバシー違反を構成する場合、イベント相関の可能性を最小限にするための措置を講じるべき(SHOULD)

## SET の利用が想定されている OpenID ファウンデーションのワーキンググループ

* [OpenID Foundation, "OpenID Risk and Incident Sharing and Coordination (RISC) Working Group"](http://openid.net/wg/risc/)

```
What is RISC WG?
The goal of RISC is to provide data sharing schemas, privacy recommendations and protocols to:

* Share information about important security events in order to thwart attackers from leveraging compromised accounts from one Service Provider to gain access to accounts on other Service Providers (mobile or web application developers and owners).　# 重要なセキュリティイベント情報の共有にSETを利用予定 
* Enable users and providers to coordinate in order to securely restore accounts following a compromise.

Internet accounts that use email addresses or phone numbers as the primary identifier for the account will be the initial focus.　
```

## (個人的に思いつく)ユースケース

アカウント周りの実装を担当してきた経験から、一通り機能を実装した後、だいたい「ユーザーの大事なアクションの履歴(アカウントアクティビティと呼んでる)を開発/QA時の調査や本運用開始後のCS対応でトラッキングできるようにログか何か残しておきたい病」にかかります。

モノリシックなサービスであればログや専用DBに格納することで事足りる気もしますが、いわゆるマイクロサービスで分散した環境、特に疎結合であればあるほどとこれのユースケースになるのかなと思います。

セキュリティ/アイデンティティに関するイベントってだけでこれを使うのは割と重量級なシステムがユースケースになりそうですが、色々なユースケース/プロファイルが出てくることに期待でしょうかね。

以上です。