オンライン認証

この記事は[Classi Advent Calendar 2018](https://qiita.com/advent-calendar/2018/classi) 21日目の記事です。

Classiのプラットフォーム開発をやっている海龍です。

技術の進歩につれて、人間社会の仮想化速度はいっそう早く進んでいると言われています。集落、国家の誕生により、あなたが生まれた途端にどこかに所属され、住民登録、印鑑登録、銀行口座作成、就職などをして始めて平和に生きて行けるようになります。

20世紀になってインターネットは誕生され、我々の行動も大きく変わりました。国家や会社などを超えたコミュニティにも簡単に繋がるようになり、情報の生産と消費もグローバル的に行われるようになりました。

仮想化された世界では、あなたはあなたであることを証明する必要があります。現実のあなたは仮想世界のあなたとの同一性を保証しないといけません。その同一性の確認行為がお粗末にすると、なりすましが容易に出来てしまい、銀行口座のお金が勝手に落とされるような大惨事にもつながります。

インターネットの世界では、あなたが真実であることを確認する行為をオンライン認証と呼んでいます。
今日は、オンライン認証の実装方式や仕様について書きたいと思います。

# オンライン認証の三要素
オンライン認証では、以下の三要素があると言われています。

- Something You Have(SYH)　持っている情報
- Something You Know(SYK)　知っている情報
- Something You Are(SYA)　生体特徴

(参考資料：https://www.ipa.go.jp/files/000040778.pdf)

多要素認証とは、上記２つ以上の要素による認証のことを指しています。一般的に利用されている２段階認証より安全だと言われています。２段階認証とは、パスワード認証後に、メールで送られてきたコードを再度入力させるような仕組みで、両方ともユーザが知っている情報なので、多要素ではなく、２段階認証だと呼ばれています。

それぞれの認証要素において、いろんな認証方式（認証メソッドとも呼ばれる）がありますが、この記事では、要素別の認証方式について体系的なご紹介を割愛して、実際、実装または利用経験があるパスワード認証、OpenID Connectによる認証、FIDO認証について紹介したいと思います。

## パスワード認証
言うまでもなくIDとパスワードを入力させ、パスワードが一致しているかを確認する方式です。認証者と被認証者の間で、秘密情報が共有されているのが前提であり、知っている情報による認証（SYK）になります。現状では、システムやWebサービスの約８割はこのパスワード認証を採用していると言われています。認証サービスの規模やアーキテクチャによって、実装方法は違います。

### パスワード検証ロジックの一例
サービス利用登録機能は、ユーザから入力されたパスワードを不可逆なハッシュにした上で、DBに格納するのが一般的なやり方です。データの漏洩が発生してもパスワードは簡単に、ばれないようにハッシュ化をしています。

検証ロジック
①　クライアントから頂いたパスワードを格納時と同じアルゴリズムでハッシュ化する
②　クライアントから頂いたIDよりDBに格納したパスワード情報を取得する
③　①と②が一致しているかを検証する

### クレデンシャル情報の格納
パスワードの検証後、生成されたクレデンシャル情報はサーバもしくはユーザエージェント側に保存されます。このクレデンシャル情報を機能（ページ）間で持ち回し、IDパスワードの再認証を回避するようになっています。

#### appサーバ側に保存するパターン

1. サーバ側はID, Passwordを受け取ってパスワード検証を行う
2. 検証は通ったらクレデンシャル情報を作成する
3. appサーバのsessionを新規作成して、処理２の情報を格納する
4. 処理３のsession idをcookieに格納する
5. 認証必要な画面からアクセスされた場合、処理３のsession idよりクレデンシャル情報sessionを取得して、検証処理を行う

クレデンシャル情報はappサーバーに保存されているため、ユーザエージェントからのアクセスは必ず同じappサーバーに接続するように、手前にあるELBの設定が必要です。サーバーのメモリリソースを消費します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/15092/85d86183-1f21-0b43-338f-7e28b4158432.png)

#### クライアント側に保存するパターン

1. サーバ側はID, Passwordを受け取ってパスワード検証を行う
2. 検証通ったらクレデンシャル情報を作成し、暗号化してクライアントローカル（cookieなど）に格納する
3. 認証必要な画面からアクセスされた場合、暗号化したクレデンシャル情報を復号して、検証処理を行う

クレデンシャル情報は、ローカルストレージとcookieのどちらに保存しても問題ありません。
cookieに保存する場合はリクエストするたびに、保存された情報は自動的にサーバに渡されます。ローカルに保存する場合は、HTTP Headerに設定してサーバーに渡します。クレデンシャル情報はクライアント側に分散されるため、サーバー側の負担は比較的に軽くなります。
また、サブドメインでサービス運用をしている場合、SSOを実現することができると考えられます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/15092/4f10d8cd-76c1-ef4a-17ba-ed7afdab335b.png)

#### RDB or KVSなどに保存するパターン

1. サーバ側はID, Passwordを受け取ってパスワード検証を行う
2. 検証通ったらクレデンシャル情報をキーペアで作成し、DBもしくはKVSに格納する。キーはcookieに格納する。
3. 認証必要な画面からアクセスされた場合、cookieにあるキーより、クレデンシャル情報を取得する

認証情報はサーバ側で管理しているため、不正があった場合、サーバ側から直接にユーザの認証を廃棄することができます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/15092/c87f86b4-adfc-acd0-c84d-69dc31341e10.png)

### パスワード認証の実装時の注意点

* 通信の暗号化（SSL）によるパスワードの保護をすべき
* パスワードは不可逆なハッシュをして格納すべき
* クレデンシャル情報はクライアント側に保存する場合は、暗号化をして格納すべき
* cookieを利用する場合はXSS脆弱の対策を取るべき（jsより読み取れないようにhttpOnlyに設定したり、https通信時だけ有効にするようにsecure属性を設定したりするなど）

## ソーシャルログイン（OpenID Connect認証）
ソーシャルログインは、既存のSNSアカウントを使って他のWebサービスにログインすることを指します。こちらの多くも、パスワードによる認証ですが、ユーザが１つのIDで複数のサービスを利用することができるようになり、煩雑な登録処理と複数のパスワードを持たなくて済むようになります。Yahoo! ID ログイン、Facebookログインというとわかりやすいでしょう。この仕組みは、OAuthやOpenID Connect(OIDC)のプロトコルで実装されることがおおい。

2017年の１年間で、ソーシャルログイン利用回数は1100万回、利用ユーザー数が370万人で、そのうちYahoo!IDが32.5%、LINEが27.1%、Facebook が20.3%、Twitterが11.9%という調査結果が出ています。
（参照資料：https://socialplus.jp/report/2018）

Classiでは、契約提携をした連携サービスに対しては、Classi ログインを提供しています。

###  処理概要
OAuthはサービスからセキュアにプロバイダー側のユーザデータにアクセスできるように、策定した認可プロトコルであり、検証の仕組みは含まれていません。ユーザ認証には利用してはいけません。OAuthを拡張したOpenID Connectプロトコルでは、署名情報付きのトークン（id_token）はプロバイダ側から渡されるようになっているため、ユーザの認証処理を行うことができます。このid_tokenからは、ユーザ認証の結果を得られます。
（OpenID Connect仕様：https://openid-foundation-japan.github.io/openid-connect-core-1_0.ja.html#Authentication）

OpenID Connect仕様の抜粋
<img src="https://qiita-image-store.s3.amazonaws.com/0/15092/dda4d49d-8f40-507c-ae85-292176bba3bc.png" width=450>

### OpenID Connectのフロー
ソーシャルログインの実装はID提供者のSDKを利用する場合が多い。ここでは、もっとも一般的に実装される認可コードフローとインプリシットフローについて説明したいと思います。

#### 認可コードフロー
このフローを利用して、認証処理を実装するサービスは、主に以下の３つの実装が必要になります。
1. ログインボタンの設置（認可リクエストを発行するリクエスト）
2. 認可コードを受け取って、トークンの発行リクエストを発行する
3. ２で発行してもらったid_tokenを検証し、ユーザを特定する情報を取得する

認可サーバから発行されるid_tokenはJSON Web Token[(RFC 7519)](https://tools.ietf.org/html/rfc7519) であり、改ざんされない且つ、URLに埋めることができます。

id_tokenのJSONペイロードサンプル

```
  {
   "iss": "https://server.example.com",
   "sub": "24400320",
   "aud": "s6BhdRkqt3",
   "nonce": "n-0S6_WzA2Mj",
   "exp": 1311281970,
   "iat": 1311280970,
   "auth_time": 1311280969
  }
```

- iss: id_token発行元
- sub: ログインユーザを識別する一意のID
- aud: id_tokenの発行対象のクライアントを識別するID
- nonce: リプレイアタック対策のパラメータ
- exp: トークンの有効期限（Unixタイム）
- iat: it_token発行時刻
- auth_time:　認証時刻

以下の図は処理シーケンスを示しています。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/15092/300564a2-3240-53da-f5a1-4d7080c1a52e.png)

仕様書に記載してある認可コードフローのステップ詳細
<img src="https://qiita-image-store.s3.amazonaws.com/0/15092/b487bec7-eba1-a3f2-87f2-f1607ee92049.png" width=450>

#### インプリシットフロー

サービス側は、プロバイダーとの間で共有する秘密情報をセキュアに保持することはできない場合、このフローを利用します。Nativeアプリからソーシャルログインを利用する場合はこのフローを使う場合が多い。

以下の図は処理シーケンスを示しています。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/15092/cb082179-56a9-19fc-ad05-97bcc58a472f.png)

仕様書に記載してある認可コードフローのステップ詳細
<img src="https://qiita-image-store.s3.amazonaws.com/0/15092/0e0c6e93-9a97-dfc7-2f58-d00ce3980feb.png" width=500>

### ソーシャルログイン実装時の注意点
* CSRF脆弱対策として、セッションの確認ができるようにstate,nonceパラメータをつけて認可リクエストを発行すべき
* Id_tokenの署名検証、発行元チェック、有効期限のチェックをすべき

## FIDO認証
「Fast IDentity Online」の略で、パスワード認証に代わるパスワードレスの認証方式として注目されています。パスワードを利用しないことで、パスワード漏洩のリスクはなくなります。また、従来のパスワード認証＋トークン認証などの多段認証の煩雑さも解消し、「安全性」と「利便性」の両立が実現できる認証技術だと言われています。
FIDO認証を実装した経験はないですが、仕様について簡単に紹介したいと思います。

### [FIDO２](https://fidoalliance.org/fido2/)
FIDO２はオンライン認証に対応していて、W3C Web AuthenticationとCTAPから構成されています。

ユーザはFIDO2対応したWebサービスにログインできるようにするには、FIDO利用登録が必要です。利用登録で、セキュリティキーやスマホなどの外部認証機の認証が要求され、その認証が通ったら、秘密鍵／公開鍵のペアを生成します。秘密鍵はトークンやスマホなど生体認証を行ったデバイスのローカルに保管され、公開鍵は、Webサービス側に共有しユーザ認証処理に利用します。ユーザの認証処理はローカルデバイス側で行われる、秘密鍵はデバイスのローカルに保存される、公開しても良い情報だけはサービス側に共有し、ネットワーク上でやりとりされるため、安全だと言えます。

#### 登録シーケンス
<img src="https://qiita-image-store.s3.amazonaws.com/0/15092/489d5a62-4a02-5985-5e30-f134f3493edf.png" width=400>

#### 認証シーケンス
<img src="https://qiita-image-store.s3.amazonaws.com/0/15092/13f20253-7ec5-d6a4-58d5-727234d823d2.png" width=400>
参考先：　https://fidoalliance.org/how-fido-works/

W3C Web Authentication　仕様：
https://www.w3.org/TR/webauthn/

CTAP(（Client-to-Authenticator Protocol）仕様：
https://fidoalliance.org/specs/fido-v2.0-rd-20161004/fido-client-to-authenticator-protocol-v2.0-rd-20161004.html

公開鍵暗号の仕組み
[公開鍵暗号と電子署名の基礎知識](https://qiita.com/kunichiko/items/ef5efdb41611d6cf7775#%E5%85%AC%E9%96%8B%E9%8D%B5%E3%81%A8%E7%A7%98%E5%AF%86%E9%8D%B5%E3%81%AE%E7%89%B9%E6%80%A7)
[公開鍵暗号とRSA暗号の仕組み](https://qiita.com/jabba/items/e5d6f826d9a8f2cefd60)

### 主要ブラウザの対応状況
Chrome、Firefox、EdgeはすでにFIDO2をサボーとしており、Safariは今日の時点では[開発中](https://webkit.org/status/#feature-web-authentication)となっています。

### FIDO2対応サービス
Google
Facebook
Yahoo! JAPAN
三菱UFJ
LINE(2019春から)

今までは、上記のようなハイテク企業がリードしてFIDOのサービス対応を進めていますが、仕様の整備、ブラウザやプラットフォームの対応が終わりつつであり、これからはFIDO対応するサービスは間違いなく増えていくと思います。先日のFIDOセミナーで、LINEの市原さんは、FIDOサーバーをオープンソース化すると名言しており、サービスへの組み込みはやりやすくなるでしょう。

長くなってしまいました。読んでいただきありがとうございます。