# LINEログインをPKCE対応する

# PKCEとは何か?

PKCE(Proof Key for Code Exchange)とは、認可コード横取り攻撃への対策を目的とし、RFC7636 (opens new window)で定義されているOAuth2.0拡張仕様です。

PKCEの機構を持たないOAuth2.0の認可フローでは、悪意のあるアプリが何らかの方法で認可コードを含むカスタムURIを取得した場合、ユーザー固有のアクセストークンを横取りされる恐れがあります。LINEログインを組み込んだウェブアプリにPKCEの認可フローを導入することで、LINEログイン v2.1のセキュリティをさらに向上させ、「認可コード横取り攻撃」を防ぐことができます。

# LINEログインにPKCEを実装するメリット

LINEログインを組み込んだウェブアプリに、PKCEを実装した場合と実装していない場合では、以下のように「認可コード横取り攻撃」に対する動作が異なります。ウェブアプリをよりセキュアにするために、PKCEの実装をおすすめします。

PKCE未実装の場合 PKCE実装済みの場合
悪意のあるアプリが何らかの方法で認可コードを含むコールバックURLを取得した場合、アクセストークンを奪えてしまいます。
PKCE未実装の場合の認可コード横取り攻撃
悪意のあるアプリにリダイレクト時に渡される情報を横取りされても、一意のcode_challengeを照合することでアクセストークンの横取りを防ぎます。
PKCE実装済みの場合の認可コード横取り攻撃
PKCEを導入するもう1つのメリット

PKCE実装済みのLINEログインを組み込んだウェブアプリに、Yahoo! JAPANアプリ (opens new window)からアクセスすると、メールアドレスとパスワードによるログインの工程をスキップできる自動ログイン機能が有効になります。

Yahoo! JAPANアプリからの自動ログイン

# LINEログインにPKCEを実装する

LINEログインにPKCEを実装するには、通常のLINEログインの組み込みの手順に加えて以下の4つの手順を行います。

PKCEの実装方法

  1. code_verifierを生成する。
  2. 手順1で生成したcode_verifierを元にcode_challengeを生成する。
  3. 手順2で生成したcode_challengecode_challenge_methodをクエリパラメータに付与した認可URLにユーザーをリダイレクトさせる。
  4. 「アクセストークンを発行する」エンドポイントのリクエストボディに手順1で生成したcode_verifierを加えて実行する。
PKCE対応のための新パラメータについて

PKCE対応のために、LINEログインの「認可URL」および「アクセストークンを発行する」エンドポイントに、以下のパラメータが追加されました。

  • code_verifier
  • code_challenge
  • code_challenge_method

各パラメータについて詳しくは、以下の各手順の説明を参照してください。

# 1. code_verifierの生成

ウェブアプリ側で、ユーザーがLINEログインを実行したタイミングで、一意のcode_verifierを生成します。code_verifierの仕様はRFC7636 (opens new window)に準拠しています。

パラメータ

パラメータ 仕様
code_verifier 使用可能文字種:半角英数字(azAZ09)および記号(-._~)からなるランダムな文字列
文字数:43文字〜128文字
wJKN8qz5t8SSI9lMFhBB6qwNkQBkuPZoCxzRhwLRUo1

サンプルコード

以下は、Node.jsを使ったcode_verifierの生成のサンプルコードです。

// randomAlphaNumericString()は、使用可能文字(半角英数字および記号)で構成された
// ランダムな文字列を引数に指定した整数分(43〜128)生成して返す関数を想定
const code_verifier = randomAlphaNumericString(43);

# 2. code_challengeの生成

生成したcode_verifierをSHA256で暗号化したうえで、Base64URL形式にエンコードすることでcode_challengeを生成できます。

パラメータ

パラメータ 仕様
code_challenge code_verifierをSHA256で暗号化したうえで、Base64URL形式にエンコードした値 BSCQwo_m8Wf0fpjmwkIKmPAJ1A7tiuRSNDnXzODS7QI
URLクエリパラメータ用に整形する

code_challengeの値は、URLクエリパラメータとして利用できるように、通常のBase64形式の文字列から以下の削除・置換を行う必要があります。詳しくは、『RFC 4648』の「5. Base 64 Encoding with URL and Filename Safe Alphabet (opens new window)」を参照してください。

  • パディング(文字詰めの=)の削除
  • +-に置換
  • /_に置換
Base64形式の例 code_challenge用に削除・置換を行った例
BSCQwo_m8Wf0fpjmwk+KmPAJ1A/tiuRSNDnXzODS7== BSCQwo_m8Wf0fpjmwk-KmPAJ1A_tiuRSNDnXzODS7

サンプルコード

以下は、Node.jsを使ったcode_challenge生成のサンプルコードです。

// このサンプルコードでは、Node.jsの"crypto"モジュールを使用しています。
// 参照:https://nodejs.org/api/crypto.html#crypto_crypto
const crypto = require("crypto");

// BASE64形式をBASE64URL形式にエンコードします。
function base64UrlEncode(str) {
    return str
        .replace(/\+/g, '-')
        .replace(/\//g, '_')
        .replace(/=/g, '');
}

// code_verifierをSHA256で暗号化し、BASE64URL形式にエンコードすることでcode_challengeを生成します。
const code_challenge = base64UrlEncode(crypto
    .createHash('sha256')
    .update(code_verifier)
    .digest('base64'));

# 3. 認可URLのクエリパラメータにcode_challengecode_challenge_methodを含める

通常のLINEログインの認可URLのクエリパラメータにcode_challengecode_challenge_methodを含めます。

パラメータ

パラメータ タイプ 必須 説明
code_challenge String 任意 手順2で生成したcode_challenge。デフォルト値はnullです(値を指定しない場合、リクエストはPKCE対応されません)。
code_challenge_method String 任意 S256(ハッシュ関数SHA256を表します。)

注:RFC7636の「4.2. Client Creates the Code Challenge」 (opens new window)では、code_challengeの生成方法として、S256以外にplain(暗号化しない)が定義されていますが、LINEログインではセキュリティ上の観点からS256のみをサポートしています。

認可URLの例

https://access.line.me/oauth2/v2.1/authorize?response_type=code&client_id=1234567890&redirect_uri=https%3A%2F%2Fexample.com%2Fauth&state=12345abcde&scope=profile%20openid&nonce=09876xyz
&code_challenge={手順2で算出したcode_challengeの値}&code_challenge_method=S256

認可URLのその他のクエリパラメータについて詳しくは、「ユーザーに認証と認可を要求する」を参照してください。

# 4. リクエストボディにcode_verifierを指定してアクセストークンを発行する

アクセストークンを発行する」エンドポイントのリクエストボディに、code_verifierを指定して実行します。

リクエストボディ

code_verifier

String

任意

手順1で生成したcode_verifier
(例:wJKN8qz5t8SSI9lMFhBB6qwNkQBkuPZoCxzRhwLRUo1

リクエストの例

curl -v -X POST https://api.line.me/oauth2/v2.1/token \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=client_credentials' \
--data-urlencode 'client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer' \
--data-urlencode 'client_assertion={JWT}'
--data-urlencode 'code_verifier={手順1で生成したcode_verifier}' 

「アクセストークンを発行する」エンドポイントについて詳しくは、『LINEログイン v2.1 APIリファレンス』の「アクセストークンを発行する」を参照してください。