目次
下のようにして、 OIDCで使う code_verifier
と code_challenge
を生成できます。 コードは Gist にもあります。
JavaScript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
// This code works on the Web Browser, because it uses Web Crypto API. const codeVerifierLength = 43; let codeVerifier = ''; const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~'; for (let i = 0; i < codeVerifierLength; i++) { codeVerifier += characters.charAt(Math.floor(Math.random() * characters.length)); } async function generateCodeChallenge(codeVerifier) { let digest = await crypto.subtle.digest( "SHA-256", new TextEncoder().encode(codeVerifier) ); return btoa(String.fromCharCode(...new Uint8Array(digest))). replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_'); } const codeChallenge = await generateCodeChallenge(codeVerifier); console.log(`Code Verifier : ${codeVerifier}`); console.log(`Code Challenge: ${codeChallenge}`); |
F#
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
open System open System.Linq let codeVerifierLength = 43 let random = Random() let chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~" let codeVerifier = Enumerable.Repeat(chars, codeVerifierLength). Select(fun s -> s.[random.Next(s.Length)]) |> String.Concat let codeChallenge = codeVerifier |> System.Text.Encoding.UTF8.GetBytes |> System.Security.Cryptography.SHA256.Create().ComputeHash |> System.Convert.ToBase64String |> fun x -> x.Replace("=", "").Replace("+", "-").Replace("/", "_") printfn "Code Verifier : %s" codeVerifier printfn "Code Challenge: %s" codeChallenge |
Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
import base64 import hashlib import random import string code_verifier_length = 43 code_verifier = ''.join(random.choice(string.ascii_letters + string.digits + '-._~')\ for i in range(code_verifier_length)) code_challenge = base64.urlsafe_b64encode(hashlib.sha256(code_verifier.encode()).digest()).\ decode().replace("=", "").replace("+", "-").replace("/", "_") print("Code Verifier :", code_verifier) print("Code Challenge:", code_challenge) |
Ruby
1 2 3 4 5 6 7 8 9 10 11 12 |
require 'digest' require 'base64' code_verifier_length = 43 o = ([('a'..'z'), ('A'..'Z'), ('0'..'9')].map(&:to_a) + %w[- _ . ~]).flatten code_verifier = (0...code_verifier_length).map { o[rand(o.length)] }.join code_challenge = Base64.urlsafe_encode64(Digest::SHA256.digest(code_verifier)). delete('=').gsub('+', '-').gsub('/', '_') puts "Code Verifier : #{code_verifier}" puts "Code Challenge: #{code_challenge}" |
Shell Script
1 2 3 4 5 6 7 |
CODE_VERIFIER_LENGTH=43 CODE_VERIFIER=`cat /dev/urandom | LC_CTYPE=C tr -dc 'a-zA-Z0-9\-._~' | fold -w $CODE_VERIFIER_LENGTH | head -n 1` CODE_CHALLENGE=`echo -n $CODE_VERIFIER | openssl dgst -sha256 -binary | openssl base64 -A | tr -d '=' | tr '/+' '_-'` echo "Code Verifier :" $CODE_VERIFIER echo "Code Challenge:" $CODE_CHALLENGE |
PKCE、Proof Key for Code Exchange
コード検証器とコードチャレンジは、PKCEメカニズムで使用される要素です。PKCEは、OAuth 2.0とOpenID Connect(OIDC)認証フローのセキュリティ強化手段です。これは、主に認可コードの傍受攻撃から保護するために使用されます。
それがどのように機能するかを以下に示します。
認可コードフロー
- 標準のOAuth 2.0認可コードフローでは、クライアントアプリケーションは認可サーバーから認可コードをリクエストします。
- 認可コードは、その後、アクセストークンと交換されます。
- ただし、このフローは、特にモバイルアプリケーションやシングルページアプリケーションでは、傍受攻撃に対して脆弱です。
PKCEの救世主
- PKCEは、認可コードをアクセストークンと交換する前に追加のステップを導入します。
- クライアントはランダムなコード検証器を生成し、そのハッシュ値(コードチャレンジ)を計算します。
- コード検証器は、初期の認可リクエスト中に認可サーバーに送信されます。
- サーバーは認可コードに関連付けられたコードチャレンジを保存します。
- クライアントが認可コードをアクセストークンと交換するとき、元のコード検証器も送信する必要があります。
- サーバーは、コード検証器が保存されたコードチャレンジと一致するかどうかを検証します。
なぜPKCEが重要なのか?
- PKCEは、認可コードを傍受してアクセストークンを取得する攻撃者を防ぎます。
- 攻撃者が認可コードを取得したとしても、対応するコード検証器を持っていません。
- これにより、トークンの盗難を行うことがより難しくなります。
使用法
- PKCEは、認可コードフローを使用するすべてのパブリッククライアント(例:モバイルアプリ、シングルページアプリ)に推奨されています。
- 機密クライアント(例:サーバーサイドアプリケーション)も追加のセキュリティとしてPKCEを使用できます。
PKCEは、あなたのアプリと認可サーバーとの間の秘密の握手のようなもので、意図した受信者のみが認可プロセスを完了できるようにします。