SAML ~ 認証コンテキストの使い方 ~

2021年のアドカレ・・・に書こうと思っていて忘れていた内容を今更。

ID厨を離れて長いので昔話。SAMLです。
今回は 認証コンテキスト のお話。

#1. 認証コンテキスト

## 1.1. 認証コンテキストとは？

コンテキスト＝文脈、つまり、どのような文脈で認証が行われたか。
より端的に言うと、**どのように認証が行われたか？** という情報です。

- Qiitaを利用しようとして、**メールアドレスとパスワードでログインした**
- ネットバンクで振込を行おうとして、**IDとパスワードでのログインに加えてワンタイムパスワードで認証した**
- 会社で利用しているサービスを使おうとして、**クライアント証明書を使ってログインした**

みたいなものです。

## 1.2. なんでSSOで認証コンテキストが必要？

上記の例にも挙げたように、単純にIDとパスワードの確認だけで提供できるサービスもあれば、より厳密な確認が必要なサービス（お金の振込など）がありますよね。

多要素認証が行われているものであればそのまま連携して利用するけれども、IDとパスワードだけ確認したものであれば、いくら信頼できるIdPから連携された情報であったとしても、追加の認証を行いたい。

といった場合のように、それぞれのサービスに適切な強度の認証を実現するため、SSOでも認証コンテキストが重要になります。


#2. SAMLの認証コンテキスト

SAMLでは、大まかに下記の3点が仕様として定められています。

- 認証コンテキストの種類
- SPからの認証要求時に認証コンテキストを指定する方法
- IdPからの認証結果に認証コンテキストを記す方法

##2.1. SAMLが定義する認証コンテキストの種類

詳細は認証コンテキストについてまとめた仕様書があるのでそちら参照。

http://docs.oasis-open.org/security/saml/v2.0/saml-authn-context-2.0-os.pdf

例えば、「3.4.9 PasswordProtectedTransport」がIDとパスワードで認証した場合の認証コンテキストですが、それを示すURIは `urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport` として定義されています。

その他、「3.4.23 SSL/TLS Certificate-Based Client Authentication」 や 「3.4.24 TimeSyncToken」など色々定義されていますが、独自に定義した認証コンテキストを使用することもできるように定められています！

##2.2. SPからの認証要求時に認証コンテキストを指定する方法

SPから必要な認証コンテキストを要求するには、`<AuthnRequest>` に下記のように `<RequestedAuthnContext>` を含めます。


```XML:AuthnRequest（一部抜粋）
<AuthnRequest [中略]>
  <RequestedAuthnContext Comparison="exact" [中略]>
    <AuthnContextClassRef [中略]>
      urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport
    </AuthnContextClassRef>
  </RequestedAuthnContext>
</AuthnRequest>
```

`<AuthnContextClassRef>` には、要求したい認証コンテキストのURIを指定。

`Comparison` には指定した認証コンテキストとの比較方法を指定します。比較方法の具体的な値は以下の通り。

- `exact`： 指定したものと同じ認証コンテキストが必要
    - 必要なもの = 指定したもの
- `minimum`：指定したもの以上のレベルの認証コンテキストが必要
    - 指定したもの ≦ 必要なもの
- `maximum`：指定したもの以下のレベルの認証コンテキストが必要
    - 必要なもの ≦ 指定したもの
- `better`：指定したものより上のレベルの認証コンテキストが必要
    - 指定したもの ＜ 必要なもの

:::note info
&lt;RequestedAuthnContext&gt; の詳細は Coreの仕様書の 3.3.2.2.1 に記述があります。
:::

http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf

## 2.3. IdPからの認証結果に認証コンテキストを記す方法
IdPで確認済みの認証コンテキストをSPに伝えるには、
`<Response>` 中の `<Assertion>` の `<AuthnStatement>` に下記のように `<AuthnContext>` を含めます。

```XML:Response（一部抜粋）
<Response [中略]>
  [中略]
  <Status>
    <StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
  </Status>
  <Assertion [中略]>
    [中略]
    <AuthnStatement AuthnInstant="2003-04-17T00:46:00Z">
      <AuthnContext>
        <AuthnContextClassRef>
          urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport
        </AuthnContextClassRef>
      </AuthnContext>
    </AuthnStatement>
  </Assertion>
</Response>
```

IdPでも未認証の場合は認証を行なってから`<Response>`を返却するように、IdPで認証済みでもSPが指定する認証コンテキストを満たしていない場合は、満たすような認証を行なってからその認証コンテキストを含んだ`<Response>`を返却します。

SPが指定する認証コンテキストを解釈できなかったり存在しない場合はエラーで `urn:oasis:names:tc:SAML:2.0:status:NoAuthnContext` を返却します。

##2.4. 使用イメージ
認証コンテキストとして、下記を定義して利用するとします。

| AuthnContextClassRef の URI   | レベル        | 認証方法 |
|:---------------|:------------:|:------------|
| `urn:hoge:ac:Password`       | 1            | パスワードによる認証          |
| `urn:hoge:ac:PKI`            | 2            | クライアント証明書による認証    |

パスワード認証が完了している状態であるとします。

その場合に、`<RequestedAuthnContext>` として下記のようなものが送信された場合、どうなるでしょうか？

1. Comparison="exact" で `urn:hoge:ac:Password` が要求された場合
1. Comparison="better" で `urn:hoge:ac:Password` が要求された場合
1. Comparison="exact" で `urn:hoge:ac:PKI` が要求された場合
1. Comparison="better" で `urn:hoge:ac:PKI` が要求された場合

以下の通りの動きとなります。

- 「1.」は、追加認証なく `<AuthnContext>` が `Password` のレスポンスが返却される
- 「2.」「3.」は追加でクライアント証明書による認証が行われ、 `<AuthnContext>` が `PKI` のレスポンスが返却される
- 「4.」は、要求された条件を満たす認証コンテキストが存在しないので、`urn:oasis:names:tc:SAML:2.0:status:NoAuthnContext` のエラーが返却される

ちょっとややこしいですが、こんなややこしいこともできるように、仕様は定義されていますので是非活用してあげてください。


#【参考】 NIST SP-800-63-3
認証のレベルが云々と出てきましたが、レベルって何？
ですよね。
詳細は NIST SP-800-63-3 をご参考ください。

Identity Proofingプロセスでのレベル（IAL:Identity Assurance Level)、Authenticationプロセスでのレベル（AAL:Authenticator Assurance Level) は、Federatedプロセスでのレベル（FAL:Federation Assurance Level）の３つの要素に分けて詳しく記されています。

https://pages.nist.gov/800-63-3/


#【参考】 OIDCの認証コンテキスト
「SAML のことはいいから、OpenID Connect のことを教えなさい。」 そんな声が聞こえてきそうなので、OpenID Connect でのことも簡単に記しておきます。

## OIDCで認証要求時に認証コンテキストを指定する方法
[AuthenticationRequest](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest) 中の `acr_values` (Requested Authentication Context Class Reference values) で同様のことができます！

## OIDCで認証結果に認証コンテキストを記す方法

[id_token](https://openid.net/specs/openid-connect-core-1_0.html#IDToken) の `acr`(Authentication Context Class Reference)、`amr`(Authentication Methods References)で同様のことができます！

##拡張仕様
認証コンテキストを意識してOpenID Connectを利用するにあたって、参考にすべき拡張仕様があるので紹介しておきます。

###IDA (Identity Assuarance)
OIDC for IDA では、Identify Verification の結果を連携する仕組みが定められており、それこそ、「NIST SP-800-63-3A の IAL2 に準拠した Identity Verificationを行なっています」 という情報が連携できたりします。

https://openid.bitbucket.io/ekyc/openid-connect-4-identity-assurance.html

###FAPI（Financial-grade API）
FAPI は "Finaincial" と名前がついてますが、金融で使うことを目的とされたものではなく、あくまで "金融で使えるくらいの高いレベルを確保できる" 拡張仕様です。

https://openid.net/specs/openid-financial-api-part-1-1_0-final.html

https://openid.net/specs/openid-financial-api-part-2-1_0.html