Oauth2.0, OpenID Connect, CSRF Study

#はじめに
Developers Summit 2015 にてOpenID Japan エバンジェリストくらさんのセッションを聞いて、認証、認可について分かっていないことが分かりました。それから数か月、各リンクを巡ってStudyした内容をまとめます。

## Study対象
モバゲ、Mixi、Facebook 各社の認証、認可シーケンスをまとめます。メジャーなセキュリティリスクとしてどんなものがあるかもまとめます。参考にした元ネタのリンクはページ以下になります。

## 参照URL
DevSumi2014 ID Token:
　http://www.slideshare.net/kura_lab/devsumi201413c5
DevSumi2015 LoginSession管理 
　http://www.slideshare.net/kura_lab/openid-connect-and-login-cycle-for-android-apps
モバゲ:
　https://docs.mobage.com/display/JPJSSDK/Login_without_Screen_Transition
Mixi:
　http://www.slideshare.net/ritou/idcon17-oauth2-csrfprotectionritou

Facebook:
　https://developers.facebook.com/docs/facebook-login/security
　https://developers.facebook.com/docs/facebook-login/access-tokens


## 用語整理
* アクセストークン: 各リソースへの認可情報
* リフレッシュトークン：各トークンをリフレッシュするためのトークン
* ID トークン：誰が誰のために何を認証したかを示すトークン
  * issuer : 発行者 Identity Provider (FB, Twitter, Yahoo, 等)となることが多いが、コンセプトを理解すれば色々な主体（例：モバイルデバイス自身等）が発行するように運用することも可能
  * audience: 誰のためにの部分 使う人、Relying Party (上記認証を使ったサービサー)とか
  * subject: 何をの部分 IdentityProviderが発行するときはEnd Userを表すことが多い
  * nonce : number used once (毎回変わる値)

* Server : 何も言わずServerといわれたときはIdentity Providerを指していることが多い
* Client :Relying Party (各種WebService)を指すことが多い。（初心者の私はエンドユーザーのことと勘違いして混乱した）


# 認証に関する脅威例
## トークン置き換え(なりすまし)
悪意のある開発者が、別のアプリで他人のアクセストークンを取得する
そのアクセストークンを置き換えてバックエンドサーバーに送信する

![attack_token_replace.png](https://qiita-image-store.s3.amazonaws.com/0/41631/88390d5b-5dd8-f082-3389-06250a38fb16.png)

原因：アクセストークンを発行したクライアント（サービス、App）を検証する仕組みがない

## Replayアタック

悪意のあるアタッカーがターゲットの通信を盗聴し、
Request部分をキャプチャーして再送することで不正に情報を取得する

対策：リクエストのなかに、毎回違う値をいれるようにする（=nonce）

## CSRF(Cross Site Request Forgeries)
イメージとしては、なりすましの逆=なりすまされ！
意図せず攻撃者のアカウントに紐づいた状態で自分のデータを保存してしまう。

![attack_csrf.png](https://qiita-image-store.s3.amazonaws.com/0/41631/dd84328f-536d-c40e-529c-1fcd4cd136cb.png)

対策：
* 認証を要求したセッションと、認可コードを送信したセッションの検証を行う

課題：
* 各ServiceServerの実装に依存する。　IDProvider側で保証しきれない


**結局、通信の各段階において送信元の正当性を検証できるかどうか。**


# mobageの例

![Mobage.png](https://qiita-image-store.s3.amazonaws.com/0/41631/4bd237e9-3850-5c14-24be-f208219622c8.png)

* state で エンドユーザーからのトークン要求の正当性を保証
* id_tokenでユーザーに関する情報を効率的にIdPからの正当性を保証しつつ3rdPartyに渡す

## モバゲのid_token例
``` json
{
    "jti" : "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
    "exp" : 1412577402,
    "a_hash" : "XXXXXXXXXXXXXXXXXXXXXX",
    "sub" : 123456,
    "aud" : !12000129-4!,
    "iss" : "https://sb-connect.mobage.jp",
    "iat" : "1412576502",
    "typ" : "id_token"
}
```
## id_tokenの検証例
* iss (Issuer Claim, この JWT を発行したサーバの識別子)が妥当か
* aud (Audience Claim, この JWT の公開範囲として指定された Client ID)がアプリケーションのClient IDと一致しているか　、自分じゃないAppを対象としたものじゃないか？
* iat (Issued At Claim)が現在時刻よりも未来の日時を表すUNIXタイムスタンプ値か？
* exp (Expiration Time Claim)が現在時刻よりも過去の日時を表すUNIXタイムスタンプ値か？
* sub (Subject Claim)がログインユーザの所持するMobageユーザーIDと一致するか


# Mixiの例

## CSRFを防止するために、ServiceServer側で、stateを発行するパターン
![Mixi-ClientState.png](https://qiita-image-store.s3.amazonaws.com/0/41631/1b7e91b3-6152-608b-cb2a-87d5b633dfc6.png)


* ServiceServer側にCSRFを防ぐ責務が集中して、IdProviderとして保証にしくい


## CSRFを防止するために、IdentityProvider側で、server_stateを発行するパターン


![Mixi-PlatformState.png](https://qiita-image-store.s3.amazonaws.com/0/41631/fe854196-157d-eb05-506a-8273d824f6f9.png)

* ServiceServerは、Sessionにserver_stateを紐づける
* IdentityProviderは、認可コードとserver_stateを紐づけて保存、認可応答には含めない
* ServiceServerはSessionにひもづくserver_stateを送信し、AccessTokenを要求
* IDProviderとして保証できる！


# Facebook
## Security Check list
下記の様なことを気をつけろと書かれています。FBに限らず参考になります。

- Never include your App Secret in client-side or decompilable code.
  - (app secret をクライアントデバイスで動くMobile/Web Appに含めるな！)
- Sign all server-to-server Graph API calls with your App Secret.
  - (全部のAPIにAppSecretで署名をつけろ！)
- Use unique short-term tokens on clients.
  - (Clientでは短いライフのトークン 使えよ！）
- Don't trust that access tokens in use by your app were actually generated by your app.
  - (あなたのAppとして作られたと保証できないAccess tokenを使うなよ！= どのサービスのためのAccessTokenか検証できる手段をもてよ）
- Reduce your app's attack surface area by locking down your Facebook app settings.
    - (Appの権限設定を最小限に落とすことで攻撃される可能性を最小化しなさい！)

## Tokens
Facebookでは以下のようなTokenの種類があります

- user access token : to access end-user's data
- app access token : to read/write app settings,  use only server-to-server call.
- page access token : to read / write/ facebook pages
- client token : limited , used rarely

## appsecret_proof
各APIごとの署名。不正な呼び出しを防止するためにもの。Server間呼出ではすべてに使うことを推奨。
```
$appsecret_proof= hash_hmac('sha256', $access_token, $app_secret); 
```

## 3rd party App,ServerからのFB利用のしかた

### Mobile Clientまで Long-lived user access tokenを返すパターン

![facebook_webclient.png](https://qiita-image-store.s3.amazonaws.com/0/41631/463cceb1-2142-e819-6002-d71f7346b178.png)


### Serverしか Long-lived user access token保持しないパターン

![facebook_callfromservder.png](https://qiita-image-store.s3.amazonaws.com/0/41631/0992092c-e290-32cd-39dd-388805cf84fa.png)


### Clientが使う時だけ Serverを介してLong-lived user access tokenを取得するパターン

![facebook_clientcode.png](https://qiita-image-store.s3.amazonaws.com/0/41631/43b35a0f-9db3-7d3e-bcee-485e64a1ed21.png)

    * （なんでShortではいけないのか？？ Shortでは権限が限られているのかな？？）


# 結局OpenID Connectの　ID_Tokenとは？　解決したいことは何でしょうか？

プリミティブに言えば、JSON web tokenです。。

``` javascript
{“typ”:”JWT”, “alg”:"HS256"} ! {
    “iss”:”https://auth.XXX.co.jp”, 
    “sub”:”123456789”, 
    “aud”:”abcdefg,
    “nonce”:”xyz”, 
    “iat”:1291836800, 
    “exp”:1300819380
}
```
今までのaccess tokenでは、何ができるとしか書いていなかったものを、
１．誰が(iss)　　２．誰のために(aud)　　３．何を(sub)　　と明記して、
誰がというのを公開暗号鍵方式で検証できるようにしたもの。

ここまでわかって読むと下記の定義がシンプルで完璧だということが理解できる。
「ID Tokenでわかること isser が audience のために subject を認証する」

## 何ができるか？
基本のコンセプトにはまるのであれば、いろいろな応用は可能。
 1. 3rdPartyServerとの通信効率化
 2. ユーザー情報の安全な受け渡し
 3. 公開鍵の安全な受け渡し

## クライアントデバイス認証への応用

一般原則：ネイティブアプリには共通鍵（secret）をうめこむべきでない。
 (サーバー間通信では可)

* Self Issued IDP
    * sub: 公開鍵のハッシュ値
    * デバイス識別ID,公開鍵を idtokenにふくめる
    * 秘密鍵を使って署名


デバイス側で秘密鍵、公開鍵を生成
公開鍵だけを渡すことで、秘密鍵をもっているデバイスを認証できる。
HoK方式（Holder of Key）
(OpenID Connect core section 7)

個人的には、ここに非常に大きな可能性を感じます。よりStudyを進めたいと思います。