PKCE で防げる「認可コード横取り攻撃」とはどのような攻撃か

# PKCE で防げる「認可コード横取り攻撃」とはどのような攻撃か

「どうすればそれを実装できるか？」は理解に易くても、
「何故そういう仕組みになったのか？」といったところに焦点を当てた丁寧な解説って、あまりなかったりしますよね。
![PKCE-1.png](https://qiita-image-store.s3.amazonaws.com/0/246440/b93f4158-1fe2-8852-a2d3-4e3b51e0223b.png)


自分自身、残念ながら腑に落としきれないまま実装することが少なくなかったり、
世の中に出回っているサービスでも、
仕様の意味をきちんと理解されていないのかパラメータが本来の目的で使われておらず隙が発生してしまっているものが存在したり･･･。

ということで、
PKCE について、自分が知っている範囲でメモ残しておこうと思います。 

## 本来あるべきを考える
### PKCEとは？

PKCEは、OAuth 2.0 の拡張仕様で、
Public Client が被り得る「認可コード横取り攻撃」を防ぐためのものです。
ぴくしーと読みます。OAuthのセキュリティを守る妖精さん:dizzy::dizzy:

仕様はこれ:point_down::point_down:
> Proof Key for Code Exchange by OAuth Public Clients
> https://tools.ietf.org/html/rfc7636

### Public Client は Implicit Flowでは？
ん:question:　Public Client ってそもそも Implicit Flowでは:question:
･･･ごもっともです:sweat_smile:

でも、なんで Public Client は Implicit Flow を使うんでしょう:question::question:

Authorization Code Flow でも ダメなわけではないと思われます。
ただ、Public Client は `client_secret` を安全に保持できる機密性を有しておらず、
`redirect_uri` が事前登録されているものであるか否かしかClient検証のポイントがありません。

ということで、Tokenエンドポイントでの `code` → `token` を省略し、
`redirect_uri` に直接 `token` を返却して通信回数を減らしているのがImplicit Flowです。

**「通信回数を減らす」**が大きな目的であって、
**「セキュリティを向上させている」**わけではないです。
（クエリではなくフラグメントで返すとかImplicitではRefreshToken発行しないとかの考慮はあります）

なので、Client認証がきちんと行えて、
Authorization Code Flowで、token取得できるに越したことないよね、
ということです。

### Public Client で Authorization Code Flow
では、Public Client で Authorization Code Flow を行った場合どうなるかを見てみましょう。

![PKCE-2.png](https://qiita-image-store.s3.amazonaws.com/0/246440/68f007c0-6910-3a76-12ff-a2aa707600e8.png)
一見、何も問題ないように見えますが、どこに攻撃する隙があるのか、見てみましょう。

## 危ない！認可コード横取り攻撃！
### 認可コード横取り攻撃とは？
ようやく出てきました。
「認可コード横取り攻撃」です。

先に記したPublic Clientで通常のAuthorization Code Flow を行った際のフローでは、4' で Browser → app に遷移する際に、隙があります。

攻撃者が、正規の「app」と同じカスタムスキーム `app://` で起動する「Malicious app」を作成してユーザの端末に何らかの形でインストールさせることに成功していた場合、下記の通り「認可コード横取り攻撃」のフローが成立してしまいます:bangbang::bangbang:

![PKCE-3.png](https://qiita-image-store.s3.amazonaws.com/0/246440/247f8fb8-5d59-3a2d-f91b-f093314379f2.png)

4' で Browser → app に遷移する際に、カスタムスキームを悪用して遷移先を横取りし、以降「Malicious app」が 正規の「app」になりすましてユーザの大事な Resource を不正に入手してしまいます。

### PKCEではどのようにして防いでいるか？

PKCEでは、`code_verifier`の値を`code_challenge_method`で指定した方法で計算して得られた値`code_challenge`が、appで計算したものと、Serverで計算したものと一致することを確認することで、リクエスト送付元の検証を行っています。

![PKCE-4.png](https://qiita-image-store.s3.amazonaws.com/0/246440/4d32da81-ebb4-6b36-bdab-f53ec1ed5ca7.png)

appからは、「2. AuthoZ Request」の際に`code_challenge_method`と`code_challenge`を送付し、
「5. Token Request」の際に`code_verifier`の値を送付します。
Server側では「2. AuthoZ Request」で受け取った値を`code`と紐付けて保存しておき、「5. Token Request」で受け取った`code_verifier`と「2. AuthoZ Request」で受け取った`code_challenge_method`で算出した値が、「2. AuthoZ Request」で受け取った`code_challenge`と一致するか確認することで、「2. AuthoZ Request」の送付元と「5. Token Request」の送付元が同じであることを検証します。

![PKCE-5.png](https://qiita-image-store.s3.amazonaws.com/0/246440/04eb3dc3-f272-1424-3471-9f5ac44ae51d.png)

「Malicious app」が「認可コード横取り攻撃」をしようとしても、Token Request送付時に、`code_verifier`の値が分からず送れない、あるいは出鱈目な値を送っても、Server側でそのようなClientを弾けますね。

![PKCE-6.png](https://qiita-image-store.s3.amazonaws.com/0/246440/0558cd7f-720e-a2ff-b70f-57dfcd994f0d.png)

## PKCEを利用しても防げない攻撃
### 「なりすまし」
PKCEを利用したら、Client認証ができるようになる:exclamation:と言うわけではありません。
あくまで「認可コードを要求したClient」と「Tokenを要求しているClient」が同一であることが検証されるのみです。

なので、「認可コードを要求」からなりすましてしまう「Malicious app 2」をユーザが利用してしまった場合は、Tokenが発行され、ユーザの大事な Resource が不正に入手されてしまいます。

![PKCE-7.png](https://qiita-image-store.s3.amazonaws.com/0/246440/21b9e00c-e02b-1d75-a11a-059f38082f0e.png)

ただこの場合、「Malicious app 2」を立ち上げたにもかかわらず、同意画面では正規の「app」に対する同意が確認されるという矛盾が発生します。

「認可コード横取り攻撃」では、正規の「app」を立ち上げて、同意画面でも正規の「app」に対する同意が確認されるにもかかわらず、「Malicious app」にTokenを発行してしまうため、ユーザから見て気付ける余地がなく防ぎようがありません。

「なりすまし」については、発生してしまう可能性はありますが、「Malicious app　2」を立ち上げているにもかかわらず、同意画面では正規の「app」に対する同意画面が表示されるため、ユーザがおかしいことに気付ける可能性が高いと思われます。

## おわりに

PKCEのより詳しい情報は下記などを参照ください。

>RFC7636として発行されたOAuth PKCEとは
> http://d.hatena.ne.jp/ritou/20151018/1445181974
>
>OAuth 2.0 and PKCE
>https://blog.pedrofelix.org/2016/02/15/oauth-2-0-and-pkce/
>
>PKCE: 認可コード横取り攻撃対策のために OAuth サーバーとクライアントが実装すべきこと
>https://qiita.com/TakahikoKawasaki/items/00f333c72ed96c4da659

Public Clientで動かす際には、PKCE対応必須で実装しておくべきですね:blush::blush: