概要
JWS(JWT系アクセストークンやAppleのレシートなど)の中身をサクッと見たいけれど、Web上のサービスを使うとログに残ったりして情報漏洩のリスクがあるのでローカルでデコードしたい場合の手順です。
環境
ダミーデータ
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
というダミーのJWSがあるとします。
中身は以下です。
header
{ "alg": "HS256", "typ": "JWT" }
payload
{ "sub": "1234567890", "name": "John Doe", "iat": 1516239022 }
signature
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
対応方法
の2通りのやり方で説明します。
ワンライナー
Base64エンコードされた文字列の長さが4の倍数でない場合、パディングとして等号(=)が必要になります。
echo "YOUR_JWS" \
| tr '.' '\n' | head -n 2 \
| awk '{ len=length($0) % 4; if (len == 2) $0=$0 "=="; else if (len == 3) $0=$0 "="; print $0; }' \
| base64 -D | jq .
ポイント
trでピリオドを改行に置き換え、それぞれのセグメントを新しい行に分割headで署名部分の破棄awkで文字列の長さをチェックし、必要に応じてパディングを追加
結果
期待通りデコードできました。
$ echo "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c" | tr '.' '\n' | head -n 2 | awk '{ len=length($0) % 4; if (len == 2) $0=$0 "=="; else if (len == 3) $0=$0 "="; print $0; }' | base64 -D | jq . { "alg": "HS256", "typ": "JWT" } { "sub": "1234567890", "name": "John Doe", "iat": 1516239022 }
コード
こちらもパディング処理を挟んだ上でデコードします。
#!/usr/bin/env python3 import base64 import json import sys def base64url_decode(input): padding = 4 - (len(input) % 4) if padding != 4: input += "=" * padding return base64.urlsafe_b64decode(input) def decode_jws(jws): header_b64, payload_b64, signature_b64 = jws.split(".") header_json = base64url_decode(header_b64).decode("utf-8") payload_json = base64url_decode(payload_b64).decode("utf-8") header = json.loads(header_json) payload = json.loads(payload_json) return header, payload, signature_b64 if __name__ == "__main__": if len(sys.argv) != 2: print("Usage: decode_jws.py <JWS>") sys.exit(1) jws_token = sys.argv[1] header, payload, signature_b64 = decode_jws(jws_token) print("Header:", json.dumps(header, indent=2)) print("Payload:", json.dumps(payload, indent=2)) print("Signature:", signature_b64)
結果
こちらも期待通りデコードできました。
$ ./main.py "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c" Header: { "alg": "HS256", "typ": "JWT" } Payload: { "sub": "1234567890", "name": "John Doe", "iat": 1516239022 } Signature: SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
まとめ
JWSの中身をローカルでサクッとデコードして確認する方法を紹介しました。