JWT(+JWS)について調べた (JWTの検証)

前回、こちらでHMAC SHA256で署名したJWT(+JWS)を作成していきましたが、今回はその検証を行っていきます。

takapi86.hatenablog.com

rfc7519 に検証手順があるので、それを参考にしつつ流れを確認しました。

  • JWTをheader、payload、signatureに分割
  • 分割したheader、payload、signatureをBASE64urlでデコード
  • headerから署名アルゴリズムを確認する
  • 確認した署名方法でJWTの検証を行なう

これに加えて有効期限の検証も行っていきます。

とにかくコードを書いていきます。 今回はコードの中で説明をしていく形になりますが、順番的にはこんな感じで検証していくのが良いかと思われます。

# -*- coding: utf-8 -*-

require 'base64'
require 'json'
require 'openssl'

jwt = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImV4cCI6MTU0MDY1MjQwMH0.a2xzydhHuo2UREDRIdcyKmlihl_1gl_BYCxdQEKmgTE'

# JWTをheader、payload、signatureに分割

encoded_header, encoded_payload, encoded_signature = jwt.split('.')

# 分割したheader、payload、signatureをBASE64urlでデコード

decoded_header = Base64.urlsafe_decode64(encoded_header) # パディングがなくてもよしなにデコードしてくれる
decoded_payload =  Base64.urlsafe_decode64(encoded_payload)
decoded_signature = Base64.urlsafe_decode64(encoded_signature)

# headerから署名アルゴリズムを確認する
# 今回は形式はJWTでHS256以外のものは対応しないので、それ以外は処理しないようにします

header = JSON.parse(decoded_header)
payload = JSON.parse(decoded_payload)

unless header['typ'] == 'JWT' && header['alg'] == 'HS256'
  puts 'error: 対象外の形式です。'
  return
end

# 確認した署名方法でJWTの検証を行なう

secret = 'secret'

verification_signature = OpenSSL::HMAC.digest(
  OpenSSL::Digest::SHA256.new,
  secret,
  "#{encoded_header}.#{encoded_payload}"
)

if decoded_signature != verification_signature
  puts 'error: 検証が失敗しました。'
  return
end

# 有効期限を設けていれば、その検証も行なう

unless Time.now.to_i < payload['exp']
  puts 'error: 有効期限が過ぎています。'
  return
end

puts '検証OK'

参考