Python JWT ถอดรหัส PyJWT
ใช้ ถอดรหัส JWT ฟรีโดยตรงในเบราว์เซอร์ของคุณ — ไม่ต้องติดตั้ง
ลอง ถอดรหัส JWT ออนไลน์ →API ทุกตัวที่ใช้การยืนยันตัวตนแบบโทเค็นจะส่ง JWT มาให้คุณในบางจุด และการหาว่ามีข้อมูลอะไรอยู่ข้างในเป็นงานที่เกิดขึ้นบ่อยมากระหว่างการพัฒนา JWT decoder ใน Python จะแปลงสตริง base64 ที่อ่านไม่ออกนั้นให้เป็น dict ของเคลมที่ใช้งานได้จริง แพ็กเกจ PyPI ที่ต้องการคือ PyJWT — ติดตั้งด้วย pip install PyJWT แต่ import ด้วย import jwt คู่มือนี้อธิบายการใช้งาน jwt.decode() ครบทุกรูปแบบ ทั้งการตรวจสอบลายเซ็นแบบสมบูรณ์ การถอดรหัสโดยไม่ใช้ secret สำหรับการตรวจสอบอย่างรวดเร็ว การถอดรหัส base64 ด้วยตนเองโดยไม่พึ่งไลบรารี การตรวจสอบคีย์สาธารณะ RS256 และข้อผิดพลาดที่พบบ่อยในระบบ auth สำหรับการตรวจสอบแบบไม่ต้องเขียนโค้ด JWT Decoder ออนไลน์ ทำสิ่งนี้ได้ทันที ตัวอย่างทั้งหมดรองรับ Python 3.10+ และ PyJWT 2.x
- ✓pip install PyJWT แล้วใช้ import jwt — ชื่อแพ็กเกจกับชื่อ import ต่างกัน ซึ่งทำให้เกือบทุกคนสะดุด
- ✓jwt.decode(token, key, algorithms=["HS256"]) คืนค่า dict ธรรมดาที่มีเคลม ต้องระบุ algorithms ทุกครั้ง
- ✓ตรวจสอบเคลมโดยไม่ยืนยัน: jwt.decode(token, options={"verify_signature": False}, algorithms=["HS256"])
- ✓สำหรับโทเค็น RSA/EC: pip install PyJWT cryptography — ต้องการ cryptography backend สำหรับอัลกอริทึมแบบ asymmetric
- ✓การถอดรหัสด้วยตนเอง (base64 + json) ใช้ได้โดยไม่ต้องพึ่งไลบรารี แต่ข้ามการตรวจสอบลายเซ็นและการหมดอายุทั้งหมด
JWT Decoding คืออะไร?
JSON Web Token ประกอบด้วยสามส่วนที่เข้ารหัสด้วย base64url คั่นด้วยจุด ได้แก่ header (อัลกอริทึมและชนิดโทเค็น) payload (เคลม — รหัสผู้ใช้ บทบาท เวลาหมดอายุ) และ signature การถอดรหัส JWT หมายถึงการแยกส่วน header และ payload นำมา base64url-decode แล้วแปลง JSON ผลลัพธ์เป็น dict ของเคลม
Header บอกว่าใช้อัลกอริทึมใดในการเซ็นโทเค็น และบางครั้งมี kid (key ID) สำหรับค้นหาคีย์ตรวจสอบที่ถูกต้อง Payload เก็บข้อมูลจริง: ผู้รับโทเค็น (sub) เวลาหมดอายุ (exp) บริการที่ออกแบบมาเพื่อ (aud) รวมถึงเคลมกำหนดเองที่แอปพลิเคชันของคุณกำหนด ส่วน signature พิสูจน์ว่าโทเค็นไม่ถูกแก้ไข แต่ต้องใช้ secret key หรือ public key ในการตรวจสอบ การถอดรหัสและการตรวจสอบเป็นการดำเนินการที่แยกจากกัน คุณสามารถถอดรหัส payload โดยไม่ตรวจสอบลายเซ็น (มีประโยชน์สำหรับการดีบัก) แต่ห้ามใช้เคลมที่ไม่ได้ตรวจสอบสำหรับการตัดสินใจด้านการอนุญาต
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c3JfOGYyYSIsInJvbGUiOiJhZG1pbiIsImV4cCI6MTcxMTgxNTYwMH0.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
{
"sub": "usr_8f2a",
"role": "admin",
"exp": 1711815600
}jwt.decode() — ถอดรหัสและตรวจสอบด้วย PyJWT
jwt.decode() คือฟังก์ชันหลักจากไลบรารี PyJWT รับสตริงโทเค็นที่เข้ารหัสแล้ว secret key (สำหรับอัลกอริทึม HMAC) หรือ public key (สำหรับ RSA/EC) และรายการ algorithms ที่จำเป็น ฟังก์ชันจะตรวจสอบลายเซ็น ตรวจสอบเคลมมาตรฐานอย่าง exp และ nbfแล้วคืนค่า payload เป็น Python dictionary หากมีอะไรผิดพลาด — ลายเซ็นไม่ถูกต้อง โทเค็นหมดอายุ อัลกอริทึมไม่ตรง — จะเกิด exception เฉพาะ
ตัวอย่างขั้นต่ำที่ใช้งานได้
import jwt
# A shared secret between the issuer and this service
SECRET_KEY = "k8s-webhook-signing-secret-2026"
# Encode a token first (simulating what an auth server would issue)
token = jwt.encode(
{"sub": "usr_8f2a", "role": "admin", "team": "platform"},
SECRET_KEY,
algorithm="HS256"
)
print(token)
# eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c3Jf...
# Decode and verify the token
payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
print(payload)
# {'sub': 'usr_8f2a', 'role': 'admin', 'team': 'platform'}
print(payload["role"])
# adminพารามิเตอร์ algorithms เป็น list ไม่ใช่สตริงเดี่ยว และบังคับใน PyJWT 2.x นี่คือฟีเจอร์ด้านความปลอดภัย: หากไม่มีพารามิเตอร์นี้ ผู้โจมตีอาจสร้างโทเค็นที่มี alg: none ใน header แล้วข้ามการตรวจสอบทั้งหมด ต้องระบุอัลกอริทึมที่แอปพลิเคชันของคุณยอมรับอย่างชัดเจน หากออก token เป็น HS256 เท่านั้น รายการควรเป็น ["HS256"] — ไม่ใช่ ["HS256", "RS256", "none"] การรายการให้แคบช่วยลดพื้นที่โจมตี
สิ่งที่ทำให้สับสนตอนแรก: PyJWT 2.x เปลี่ยน jwt.encode() ให้คืนค่าเป็นสตริงแทน bytes หากอ่านคำตอบเก่าจาก Stack Overflow ที่เรียก .decode("utf-8") กับโทเค็นที่เข้ารหัสแล้ว โค้ดนั้นมาจากยุค PyJWT 1.x และจะเกิด AttributeError บน version 2.x โทเค็นเป็นสตริงอยู่แล้ว — ใช้ได้โดยตรง
ตัวอย่างครบวงจรพร้อม Expiration
import jwt
from datetime import datetime, timedelta, timezone
SECRET_KEY = "webhook-processor-secret"
# Create a token that expires in 1 hour
payload = {
"sub": "svc_payment_processor",
"iss": "auth.internal.example.com",
"aud": "https://api.example.com",
"exp": datetime.now(timezone.utc) + timedelta(hours=1),
"permissions": ["orders:read", "refunds:create"],
}
token = jwt.encode(payload, SECRET_KEY, algorithm="HS256")
# Later, when the token arrives in a request header:
try:
decoded = jwt.decode(
token,
SECRET_KEY,
algorithms=["HS256"],
audience="https://api.example.com",
issuer="auth.internal.example.com",
)
print(f"Service: {decoded['sub']}")
print(f"Permissions: {decoded['permissions']}")
except jwt.ExpiredSignatureError:
print("Token expired — request re-authentication")
except jwt.InvalidAudienceError:
print("Token not intended for this API")
except jwt.InvalidIssuerError:
print("Token issued by unknown authority")datetime เป็น Unix timestamp โดยอัตโนมัติ ระหว่างการเข้ารหัส ระหว่างการถอดรหัส เคลม exp, iat และ nbf จะคืนค่าเป็น integer ไม่ใช่ออบเจกต์ datetime ต้องแปลงด้วยตนเองด้วย datetime.fromtimestamp(payload["exp"], tz=timezone.utc)ถอดรหัส JWT โดยไม่ตรวจสอบลายเซ็น
บางครั้งคุณต้องอ่านเคลมก่อนที่จะตรวจสอบโทเค็น สถานการณ์ที่พบบ่อย: header ของโทเค็นมีฟิลด์ kid (key ID) และคุณต้องดึง public key ที่ตรงกันจาก JWKS endpoint ก่อนจึงจะตรวจสอบได้ PyJWT รองรับสิ่งนี้ด้วยออปชัน verify_signature: False คุณยังต้องส่งรายการ algorithms แต่อาร์กิวเมนต์ key จะถูกละเว้น
import jwt
token = (
"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InNpZy0xNzI2In0"
".eyJzdWIiOiJ1c3JfM2M3ZiIsInNjb3BlIjoicmVhZDpvcmRlcnMiLCJpc3MiOiJhdXRoLmV4YW1wbGUuY29tIn0"
".signature_placeholder"
)
# Step 1: Read claims without verification to get routing info
unverified = jwt.decode(
token,
options={"verify_signature": False},
algorithms=["RS256"]
)
print(unverified)
# {'sub': 'usr_3c7f', 'scope': 'read:orders', 'iss': 'auth.example.com'}
# Step 2: Read the header to find which key to use
header = jwt.get_unverified_header(token)
print(header)
# {'alg': 'RS256', 'typ': 'JWT', 'kid': 'sig-1726'}
# Now use header['kid'] to fetch the correct public key from your JWKS endpointมีความแตกต่างที่ละเอียดอ่อนตรงนี้ jwt.get_unverified_header() อ่านเฉพาะ header — ส่วนแรก ส่วนการเรียก jwt.decode() ด้วย verify_signature: False อ่าน payload (ส่วนที่สอง) เมื่อใช้ทั้งสองอย่างร่วมกัน คุณสามารถดึงข้อมูลทุกอย่าง จากโทเค็นโดยไม่ต้องใช้คีย์ PyJWT ยังคงตรวจสอบว่าโทเค็นมีโครงสร้างที่ถูกต้อง (สามส่วนคั่นด้วยจุด base64 ถูกต้อง JSON ถูกต้อง) แม้จะปิดการตรวจสอบลายเซ็น หากโทเค็นมีโครงสร้างผิดพลาด จะเกิด DecodeError โดยไม่คำนึงถึงออปชันที่ส่งมา
เอกสารอ้างอิงพารามิเตอร์ jwt.decode()
ลายเซ็นเต็มคือ jwt.decode(jwt, key, algorithms, options, audience, issuer, leeway, require) พารามิเตอร์ทั้งหมดหลัง algorithms เป็น keyword-only
Dict options ให้การควบคุมอย่างละเอียดว่า PyJWT จะดำเนินการตรวจสอบใดบ้าง คีย์แมปกับการตรวจสอบแต่ละอย่าง: verify_signature, verify_exp, verify_nbf, verify_iss, verify_aud และ verify_iat ค่าเริ่มต้นทั้งหมดเป็น True เว้นแต่คุณตั้งค่าเป็น False อย่างชัดเจน ใน production ควรปล่อยให้ค่าเหล่านี้เป็นค่าเริ่มต้น ควรปิดการตรวจสอบเฉพาะบางอย่างเฉพาะตอนพัฒนาเมื่อทำงานกับโทเค็นทดสอบที่หมดอายุแล้ว และต้องการข้ามการตรวจสอบการหมดอายุชั่วคราว
import jwt
# Require specific claims to be present — raises MissingRequiredClaimError if absent
payload = jwt.decode(
token,
SECRET_KEY,
algorithms=["HS256"],
options={"require": ["exp", "iss", "sub"]},
issuer="auth.internal.example.com",
)
# During development only: skip expiry to test with old tokens
dev_payload = jwt.decode(
token,
SECRET_KEY,
algorithms=["HS256"],
options={"verify_exp": False}, # DO NOT use in production
)การถอดรหัส JWT ด้วยตนเองโดยใช้ base64 และ json
คุณสามารถถอดรหัส JWT payload โดยใช้เฉพาะ Python standard library — ไม่ต้อง pip install วิธีนี้มีประโยชน์จริงในหลายสถานการณ์: สคริปต์ดีบักที่การเพิ่ม dependency เป็นเรื่องเกินความจำเป็น สภาพแวดล้อม CI ที่จำกัดซึ่งมีเฉพาะ standard library AWS Lambda functions ที่ต้องการลด cold start time หรือเพียงแค่ต้องการเข้าใจว่า JWT คืออะไรภายใต้ประทุน กระบวนการนั้นตรงไปตรงมา: แบ่งด้วยจุด ดึงส่วนที่ต้องการ เพิ่ม base64 padding ถอดรหัส และแปลง JSON
โมดูล base64 และ json ทั้งคู่อยู่ใน Python standard library ดังนั้นวิธีนี้ใช้ได้กับทุก Python installation ตั้งแต่ 3.6 เป็นต้นมา ฟังก์ชันด้านล่างจัดการทั้ง header (ส่วนแรก) และ payload (ส่วนที่สอง) แยกกัน:
import base64
import json
def decode_jwt_payload(token: str) -> dict:
"""Decode the JWT payload without signature verification.
Works with any JWT — HS256, RS256, ES256, etc.
"""
parts = token.split(".")
if len(parts) != 3:
raise ValueError(f"Expected 3 JWT segments, got {len(parts)}")
payload_b64 = parts[1]
# base64url uses - and _ instead of + and /
# Python's urlsafe_b64decode handles this, but needs padding
payload_b64 += "=" * (-len(payload_b64) % 4)
payload_bytes = base64.urlsafe_b64decode(payload_b64)
return json.loads(payload_bytes)
def decode_jwt_header(token: str) -> dict:
"""Decode the JWT header (algorithm, key ID, type)."""
header_b64 = token.split(".")[0]
header_b64 += "=" * (-len(header_b64) % 4)
return json.loads(base64.urlsafe_b64decode(header_b64))
# Example usage
token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c3JfOGYyYSIsInJvbGUiOiJhZG1pbiIsImV4cCI6MTcxMTgxNTYwMH0.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"
header = decode_jwt_header(token)
print(f"Algorithm: {header['alg']}")
# Algorithm: HS256
claims = decode_jwt_payload(token)
print(f"Subject: {claims['sub']}")
print(f"Role: {claims['role']}")
# Subject: usr_8f2a
# Role: adminเทคนิค padding (+= "=" * (-len(s) % 4)) คือส่วนที่ทุกคนมักลืม JWT base64url ตัดอักขระ = ท้ายออก แต่ urlsafe_b64decode ของ Python ต้องการ หากไม่แก้ padding จะเกิด binascii.Error: Incorrect padding
jwt.decode() กับคีย์จริงถอดรหัส JWT จาก API Response และไฟล์โทเค็น
สองสถานการณ์จริงที่พบบ่อยที่สุด: การแยก JWT จาก HTTP response (OAuth token endpoint หรือ login API) และการอ่านโทเค็นจากไฟล์ (service account credentials, Kubernetes mounted secrets, โทเค็นที่แคชไว้บนดิสก์) ทั้งสองต้องการการจัดการข้อผิดพลาดที่เหมาะสม network request อาจล้มเหลว ไฟล์อาจหาย โทเค็นอาจหมดอายุระหว่างที่แคชไว้กับเวลาที่อ่าน
ตัวอย่างด้านล่างใช้ httpx สำหรับ HTTP calls (เปลี่ยนเป็น requests ได้หากต้องการ รูปแบบเหมือนกัน) และ pathlib.Path สำหรับการดำเนินการกับไฟล์ แต่ละตัวอย่างจัดการ PyJWT exception เฉพาะ แทนที่จะใช้ except Exception เพื่อให้ตอบสนองต่อแต่ละโหมดความล้มเหลวได้อย่างเหมาะสม: ยืนยันตัวตนใหม่เมื่อหมดอายุ แจ้งเตือนเมื่อลายเซ็นล้มเหลว retry เมื่อ network หมดเวลา
ถอดรหัส JWT จาก API Response
import jwt
import httpx # or requests
TOKEN_ENDPOINT = "https://auth.example.com/oauth/token"
SECRET_KEY = "shared-webhook-signing-key"
def get_and_decode_token() -> dict:
"""Fetch an access token from the auth server and decode it."""
try:
response = httpx.post(
TOKEN_ENDPOINT,
data={
"grant_type": "client_credentials",
"client_id": "svc_order_processor",
"client_secret": "cs_9f3a7b2e",
},
timeout=10.0,
)
response.raise_for_status()
except httpx.HTTPError as exc:
raise RuntimeError(f"Token request failed: {exc}") from exc
token_data = response.json()
access_token = token_data["access_token"]
try:
payload = jwt.decode(
access_token,
SECRET_KEY,
algorithms=["HS256"],
audience="https://api.example.com",
)
return payload
except jwt.InvalidTokenError as exc:
raise RuntimeError(f"Invalid token from auth server: {exc}") from exc
claims = get_and_decode_token()
print(f"Service: {claims['sub']}, Scopes: {claims.get('scope', 'none')}")ถอดรหัส JWT จากไฟล์
import jwt
from pathlib import Path
from datetime import datetime, timezone
TOKEN_PATH = Path("/var/run/secrets/service-account-token")
PUBLIC_KEY_PATH = Path("/etc/ssl/auth/public_key.pem")
def decode_token_from_file() -> dict:
"""Read a JWT from a file, verify with a PEM public key."""
try:
token = TOKEN_PATH.read_text().strip()
public_key = PUBLIC_KEY_PATH.read_text()
except FileNotFoundError as exc:
raise RuntimeError(f"Missing file: {exc.filename}") from exc
try:
payload = jwt.decode(
token,
public_key,
algorithms=["RS256"],
audience="https://internal-api.example.com",
)
except jwt.ExpiredSignatureError:
exp_time = jwt.decode(
token,
options={"verify_signature": False},
algorithms=["RS256"],
).get("exp", 0)
expired_at = datetime.fromtimestamp(exp_time, tz=timezone.utc)
raise RuntimeError(f"Token expired at {expired_at.isoformat()}")
except jwt.InvalidTokenError as exc:
raise RuntimeError(f"Token verification failed: {exc}") from exc
return payload
claims = decode_token_from_file()
print(f"Subject: {claims['sub']}, Issuer: {claims['iss']}")การถอดรหัส JWT ผ่าน Command-Line
บางครั้งคุณแค่ต้องการดูโทเค็นจาก terminal โดยไม่ต้องเขียนสคริปต์ อาจเป็นตอนดีบัก OAuth flow และต้องการดูว่ามีอะไรใน header Authorization หรือดึงโทเค็นจาก browser DevTools แล้วต้องการตรวจสอบการหมดอายุ แฟล็ก -c ของ Python ทำให้ทำได้เป็น one-liner ส่งโทเค็นเข้าแล้วรับเคลมเป็น JSON ที่จัดรูปแบบแล้ว ไม่ต้องไฟล์สคริปต์ ไม่ต้อง virtual environment
# Decode JWT payload from clipboard or variable (no verification)
echo "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c3JfOGYyYSIsInJvbGUiOiJhZG1pbiJ9.sig" \
| python3 -c "
import sys, base64, json
token = sys.stdin.read().strip()
payload = token.split('.')[1]
payload += '=' * (-len(payload) % 4)
print(json.dumps(json.loads(base64.urlsafe_b64decode(payload)), indent=2))
"
# {
# "sub": "usr_8f2a",
# "role": "admin"
# }# Decode JWT header to check algorithm and key ID
echo "eyJhbGciOiJSUzI1NiIsImtpZCI6InNpZy0xNzI2In0.payload.sig" \
| python3 -c "
import sys, base64, json
token = sys.stdin.read().strip()
header = token.split('.')[0]
header += '=' * (-len(header) % 4)
print(json.dumps(json.loads(base64.urlsafe_b64decode(header)), indent=2))
"
# {
# "alg": "RS256",
# "kid": "sig-1726"
# }# If PyJWT is installed, verify and decode in one step
python3 -c "
import jwt, sys, json
token = sys.argv[1]
payload = jwt.decode(token, options={'verify_signature': False}, algorithms=['HS256'])
print(json.dumps(payload, indent=2))
" "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c3JfOGYyYSJ9.sig"สำหรับทางเลือกแบบ visual โดยไม่ต้องตั้งค่า terminal วาง token ของคุณลงใน ToolDeck JWT Decoder แล้วดู header, payload และสถานะการตรวจสอบลายเซ็นได้ทันที
python-jose และทางเลือกอื่น
python-jose เป็นไลบรารี JWT ทางเลือกที่รองรับ JWS, JWE (โทเค็นที่เข้ารหัส) และ JWK โดยตรง หากแอปพลิเคชันของคุณต้องจัดการ JWE (encrypted JWT) — ที่ payload เองถูกเข้ารหัส ไม่ใช่แค่เซ็น — python-jose คือตัวเลือกที่เหมาะสมเพราะ PyJWT ไม่รองรับ JWE เลย ไลบรารีนี้ยังมีการจัดการ JWKS key set ในตัว ซึ่งทำให้การรวมกับ identity provider อย่าง Auth0, Okta หรือ Keycloak ที่มี rotating key sets ง่ายขึ้น interface การถอดรหัสเกือบเหมือนกับ PyJWT ดังนั้นการเปลี่ยนระหว่างกันต้องการการแก้ไขโค้ดน้อยมาก:
# pip install python-jose[cryptography]
from jose import jwt as jose_jwt
token = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c3JfOGYyYSIsInNjb3BlIjoib3JkZXJzOnJlYWQifQ.signature"
# Verified decode — same pattern as PyJWT
payload = jose_jwt.decode(
token,
"signing-secret-key",
algorithms=["HS256"],
audience="https://api.example.com",
)
print(payload)
# {'sub': 'usr_8f2a', 'scope': 'orders:read'}
# Unverified decode
claims = jose_jwt.get_unverified_claims(token)
header = jose_jwt.get_unverified_header(token)
print(f"Algorithm: {header['alg']}, Subject: {claims['sub']}")คำแนะนำ: เริ่มต้นด้วย PyJWT ครอบคลุม 95% ของ use case ของ JWT มีชุมชนขนาดใหญ่ที่สุด และ API สะอาด เปลี่ยนไปใช้ python-jose หากต้องการรองรับ JWE หรือต้องการการจัดการ JWKS ทางเลือกที่สามที่ควรกล่าวถึงคือ Authlib ซึ่งรวม JWT handling ไว้ใน OAuth/OIDC framework ขนาดใหญ่กว่า หากคุณใช้ Authlib อยู่แล้วสำหรับ OAuth client flows โมดูล authlib.jose.jwt ของมันช่วยให้ไม่ต้องเพิ่ม JWT dependency ที่สอง มิฉะนั้น มันเป็น dependency ที่หนักเกินไปสำหรับแค่การถอดรหัสโทเค็น
แสดงผล Terminal พร้อม Syntax Highlighting
การอ่านเคลม JWT แบบ raw ใน terminal ก็พอสำหรับการตรวจสอบเร็ว ๆ แต่เมื่อคุณ ดีบัก token payload บ่อย ๆ (ผู้เขียนทำสิ่งนี้ทุกวันระหว่างสร้าง internal auth gateway) output ที่มีสีทำให้ต่างกันมาก ค่าสตริง ตัวเลข บูลีน และ null แสดงด้วยสีที่แตกต่างกัน ทำให้มองเห็น permission ที่หายไปหรือ timestamp การหมดอายุที่ผิดได้ทันที โดยไม่ต้องอ่านทุกอักขระ
ไลบรารี rich (pip install rich) มีฟังก์ชัน print_json ที่รับทั้ง JSON string หรือ Python dict แล้วพิมพ์พร้อม syntax highlighting เต็มรูปแบบไปยัง terminal รวมกับ PyJWT สำหรับ workflow การตรวจสอบ JWT แบบสองบรรทัด:
# pip install rich PyJWT
import jwt
from rich import print_json
token = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c3JfOGYyYSIsInJvbGUiOiJhZG1pbiIsInBlcm1pc3Npb25zIjpbIm9yZGVyczpyZWFkIiwicmVmdW5kczpjcmVhdGUiXSwiZXhwIjoxNzExODE1NjAwfQ.sig"
payload = jwt.decode(
token,
options={"verify_signature": False},
algorithms=["HS256"]
)
# Colorized, indented JSON output in the terminal
print_json(data=payload)
# {
# "sub": "usr_8f2a", ← strings in green
# "role": "admin",
# "permissions": [
# "orders:read",
# "refunds:create"
# ],
# "exp": 1711815600 ← numbers in cyan
# }rich มี ANSI escape codes ห้ามเขียนลงไฟล์หรือคืนค่าจาก API endpoint — ใช้สำหรับแสดงผลบน terminal เท่านั้น ใช้ json.dumps() เมื่อต้องการ plain text outputการประมวลผล Token จำนวนมาก
JWT token แต่ละตัวมีขนาดเล็ก (โดยทั่วไปน้อยกว่า 2 KB) แต่มีสถานการณ์ที่คุณต้องประมวลผลทีละมาก ๆ การวิเคราะห์ audit log หลังเกิดเหตุการณ์ด้านความปลอดภัย สคริปต์ session migration เมื่อเปลี่ยน auth provider การตรวจสอบ compliance เป็นชุดที่คุณต้องพิสูจน์ว่าทุก token ที่ออกใน 90 วันที่ผ่านมาเซ็นด้วยคีย์ที่ถูกต้อง หากคุณมี token หลายหมื่นตัวในไฟล์ NDJSON log การอ่านทีละบรรทัดช่วยหลีกเลี่ยงการโหลดไฟล์ทั้งหมดเข้า memory และรายงานผลลัพธ์ได้แบบทีละส่วน
ตรวจสอบ Token จาก Audit Log เป็นชุด
import jwt
import json
from pathlib import Path
SECRET_KEY = "audit-log-signing-key"
def validate_token_log(log_path: str) -> dict:
"""Process an NDJSON file where each line has a 'token' field.
Returns counts of valid, expired, and invalid tokens.
"""
stats = {"valid": 0, "expired": 0, "invalid": 0}
with open(log_path) as fh:
for line_num, line in enumerate(fh, 1):
line = line.strip()
if not line:
continue
try:
record = json.loads(line)
token = record["token"]
except (json.JSONDecodeError, KeyError):
stats["invalid"] += 1
continue
try:
jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
stats["valid"] += 1
except jwt.ExpiredSignatureError:
stats["expired"] += 1
except jwt.InvalidTokenError:
stats["invalid"] += 1
return stats
result = validate_token_log("auth-events-2026-03.ndjson")
print(f"Valid: {result['valid']}, Expired: {result['expired']}, Invalid: {result['invalid']}")
# Valid: 14832, Expired: 291, Invalid: 17แยกเคลมจาก NDJSON Token Export
import base64
import json
from datetime import datetime, timezone
def extract_claims_stream(input_path: str, output_path: str):
"""Read tokens line by line, decode payloads, write flattened claims."""
with open(input_path) as infile, open(output_path, "w") as outfile:
for line in infile:
line = line.strip()
if not line:
continue
record = json.loads(line)
token = record.get("access_token", "")
parts = token.split(".")
if len(parts) != 3:
continue
payload_b64 = parts[1] + "=" * (-len(parts[1]) % 4)
claims = json.loads(base64.urlsafe_b64decode(payload_b64))
# Flatten into an audit-friendly record
flat = {
"timestamp": record.get("timestamp"),
"subject": claims.get("sub"),
"issuer": claims.get("iss"),
"expired_at": datetime.fromtimestamp(
claims.get("exp", 0), tz=timezone.utc
).isoformat(),
}
outfile.write(json.dumps(flat) + "\n")
extract_claims_stream("token-audit.ndjson", "claims-extract.ndjson")multiprocessing.Pool เพื่อกระจายการตรวจสอบไปยังหลาย core เนื่องจากแต่ละโทเค็นเป็นอิสระจากกันข้อผิดพลาดที่พบบ่อย
ข้อผิดพลาดสี่อย่างนี้เกิดขึ้นซ้ำ ๆ ใน code review และคำถามบน Stack Overflow แต่ละอย่างเกิดขึ้นได้ง่าย และข้อความ error ที่ PyJWT ให้ไม่ได้ชี้ตรงไปยังสาเหตุเสมอไป ปัญหาการตั้งชื่อแพ็กเกจเพียงอย่างเดียวอาจเสียเวลาดีบักหลายชั่วโมง เมื่อมีคนติดตั้ง library ผิดตัวแล้วได้ API ที่ไม่คาดคิด
ปัญหา: การรัน pip install jwt หรือ pip install python-jwt ติดตั้งแพ็กเกจคนละตัวกันเลย จากนั้น import jwt จะล้มเหลวหรือให้ API ที่ไม่รู้จัก
วิธีแก้ไข: ติดตั้งด้วย pip install PyJWT เสมอ การ import คือ import jwt ตรวจสอบด้วย pip show PyJWT เพื่อยืนยันแพ็กเกจที่ถูกต้อง
pip install jwt # or pip install python-jwt import jwt # wrong package — different API entirely
pip install PyJWT import jwt # correct — this is PyJWT print(jwt.__version__) # 2.x
ปัญหา: ใน PyJWT 1.x พารามิเตอร์ algorithms เป็น optional และค่าเริ่มต้นอนุญาตอัลกอริทึมใดก็ได้ ทำให้เกิดช่องโหว่ด้านความปลอดภัยที่ผู้โจมตีสามารถตั้ง alg: none ได้ PyJWT 2.x ตอนนี้จะเกิด DecodeError หาก algorithms ไม่มี
วิธีแก้ไข: ส่ง algorithms เป็น list เฉพาะเจาะจงเสมอ ใช้เฉพาะอัลกอริทึมที่แอปพลิเคชันของคุณออก token ด้วยจริง ๆ
# PyJWT 2.x — this raises DecodeError payload = jwt.decode(token, SECRET_KEY) # DecodeError: algorithms must be specified
payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
ปัญหา: การส่ง string secret ไปยัง jwt.decode() พร้อม algorithms=["RS256"] จะเกิด InvalidSignatureError RS256 ต้องการ PEM-encoded public key ไม่ใช่ shared secret string
วิธีแก้ไข: โหลด PEM public key จากไฟล์หรือ environment variable ติดตั้งแพ็กเกจ cryptography: pip install PyJWT cryptography
# This fails — RS256 needs a public key, not a string secret payload = jwt.decode(token, "my-secret", algorithms=["RS256"]) # InvalidSignatureError
public_key = open("public_key.pem").read()
payload = jwt.decode(token, public_key, algorithms=["RS256"])ปัญหา: การเข้ารหัส base64url ของ JWT ตัด = ท้ายออก base64.urlsafe_b64decode ของ Python จะเกิด binascii.Error: Incorrect padding หากส่ง segment แบบ raw โดยไม่แก้ padding
วิธีแก้ไข: เพิ่ม padding ก่อน decode: segment += '=' * (-len(segment) % 4) สูตรนี้ให้จำนวน padding characters ที่ถูกต้องเสมอ (0, 1, 2 หรือ 3)
import base64
payload_b64 = token.split(".")[1]
data = base64.urlsafe_b64decode(payload_b64)
# binascii.Error: Incorrect paddingimport base64
payload_b64 = token.split(".")[1]
payload_b64 += "=" * (-len(payload_b64) % 4)
data = base64.urlsafe_b64decode(payload_b64) # worksPyJWT เทียบกับทางเลือกอื่น — เปรียบเทียบอย่างรวดเร็ว
PyJWT คือจุดเริ่มต้นที่เหมาะสมสำหรับแอปพลิเคชัน Python ส่วนใหญ่ รองรับ HMAC และ (ด้วย cryptography backend) การตรวจสอบลายเซ็น RSA และ EC หากต้องการ JWE (encrypted tokens) เปลี่ยนไปใช้ python-jose หรือ Authlib การถอดรหัสด้วย base64 ด้วยตนเองใช้ได้สำหรับดีบักแต่ไม่มีการรับประกันด้านความปลอดภัยใด ๆ
เมื่อใดควรใช้ตัวเลือกใด: PyJWT สำหรับ web service มาตรฐานที่ทำ HS256 หรือ RS256 verification python-jose เมื่อสถาปัตยกรรมรวม encrypted tokens หรือ JWKS rotation base64 ด้วยตนเองสำหรับการตรวจสอบเร็วในสภาพแวดล้อมที่ pip ใช้ไม่ได้ (CI containers, production hosts ที่จำกัด, AWS Lambda cold starts ที่ต้องการลด dependencies) Authlib เมื่อโปรเจกต์ใช้มันอยู่แล้วสำหรับ OAuth client flows และการเพิ่ม JWT library อีกตัวจะซ้ำซ้อน
สำหรับทางเลือกที่ไม่ต้องเขียนโค้ด วาง token ใด ๆ ลงใน JWT Decoder เพื่อดู header และ payload ที่ถอดรหัสแล้วพร้อม claim validation feedback
คำถามที่พบบ่อย
จะถอดรหัส JWT ใน Python โดยไม่ตรวจสอบลายเซ็นได้อย่างไร?
ส่ง options={"verify_signature": False} และ algorithms=["HS256"] ไปยัง jwt.decode() ฟังก์ชันจะคืนค่า dict ของเพย์โหลดโดยไม่ตรวจสอบลายเซ็น ใช้วิธีนี้เฉพาะเพื่อตรวจสอบเคลมก่อนดึงคีย์สาธารณะที่ถูกต้อง หรือดีบักในสภาพแวดล้อมการพัฒนาเท่านั้น ห้ามข้ามการตรวจสอบสำหรับโทเค็นที่ใช้ควบคุมการเข้าถึงทรัพยากรใดก็ตาม
import jwt
token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c3JfOGYyYSIsInJvbGUiOiJhZG1pbiJ9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
payload = jwt.decode(
token,
options={"verify_signature": False},
algorithms=["HS256"]
)
print(payload)
# {'sub': 'usr_8f2a', 'role': 'admin'}PyJWT และ python-jwt แตกต่างกันอย่างไร?
PyJWT (pip install PyJWT, import jwt) คือไลบรารี JWT ที่ได้รับความนิยมมากที่สุดสำหรับ Python มีการดาวน์โหลดมากกว่า 80 ล้านครั้งต่อเดือน ส่วน python-jwt (pip install python_jwt) เป็นไลบรารีแยกต่างหากที่ใช้กันน้อยกว่ามากและมี API แตกต่างกัน ถ้าเห็น import jwt ในโค้ดของใครก็ตาม แสดงว่าเขาใช้ PyJWT ความสับสนในการตั้งชื่อมาจากชื่อแพ็กเกจ PyPI (PyJWT) ที่ต่างจากชื่อ import (jwt) ให้ใช้ PyJWT เว้นแต่มีเหตุผลเฉพาะเจาะจง
จะถอดรหัส JWT แบบ RS256 ใน Python ได้อย่างไร?
ติดตั้งทั้ง PyJWT และ cryptography backend: pip install PyJWT cryptography จากนั้นส่งคีย์สาธารณะแบบ PEM เป็นอาร์กิวเมนต์ key และระบุ algorithms=["RS256"] PyJWT จะมอบหมายการตรวจสอบลายเซ็น RSA ให้กับไลบรารี cryptography หากไม่ได้ติดตั้งแพ็กเกจ cryptography PyJWT จะเกิดข้อผิดพลาดเมื่อพยายามใช้อัลกอริทึม RSA หรือ EC
import jwt
public_key = open("public_key.pem").read()
payload = jwt.decode(
token,
public_key,
algorithms=["RS256"],
audience="https://api.example.com"
)ทำไม PyJWT ถึงเกิด ExpiredSignatureError?
PyJWT ตรวจสอบเคลม exp (expiration) โดยค่าเริ่มต้น หากเวลา UTC ปัจจุบันเกินกว่า timestamp ของ exp จะเกิด jwt.ExpiredSignatureError คุณสามารถเพิ่มค่าความคลาดเคลื่อนของนาฬิกาด้วยพารามิเตอร์ leeway: jwt.decode(token, key, algorithms=["HS256"], leeway=timedelta(seconds=30)) ซึ่งให้ระยะเวลาผ่อนผัน 30 วินาที หากต้องการปิดการตรวจสอบการหมดอายุทั้งหมด (ไม่แนะนำสำหรับ production) ให้ส่ง options={"verify_exp": False}
import jwt
from datetime import timedelta
try:
payload = jwt.decode(token, secret, algorithms=["HS256"], leeway=timedelta(seconds=30))
except jwt.ExpiredSignatureError:
print("Token has expired — prompt re-authentication")สามารถอ่านเคลม JWT โดยไม่ใช้ไลบรารีใด ๆ ใน Python ได้ไหม?
ได้ แบ่งโทเค็นด้วยจุด นำส่วนที่สอง (เพย์โหลด) มาเติมเครื่องหมาย = เพื่อให้ความยาวเป็นผลคูณของ 4 จากนั้น base64url-decode และแปลง JSON วิธีนี้ให้ข้อมูลเคลมแต่ไม่มีการตรวจสอบลายเซ็น มีประโยชน์ในสภาพแวดล้อมจำกัดที่ไม่สามารถติดตั้ง PyJWT หรือสำหรับสคริปต์ดีบักอย่างรวดเร็ว
import base64, json
token = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c3JfOGYyYSJ9.signature"
payload_b64 = token.split(".")[1]
payload_b64 += "=" * (-len(payload_b64) % 4) # fix padding
claims = json.loads(base64.urlsafe_b64decode(payload_b64))
print(claims) # {'sub': 'usr_8f2a'}จะตรวจสอบเคลม audience ด้วย PyJWT ได้อย่างไร?
ส่งพารามิเตอร์ audience ไปยัง jwt.decode(): jwt.decode(token, key, algorithms=["HS256"], audience="https://api.example.com") PyJWT จะเปรียบเทียบเคลม aud ในโทเค็นกับค่าที่ระบุ หากโทเค็นไม่มีเคลม aud หรือค่าไม่ตรงกัน จะเกิด jwt.InvalidAudienceError คุณยังสามารถส่งรายการ audience ที่ยอมรับได้หลายค่าหากบริการของคุณรับโทเค็นสำหรับ API หลายตัว
import jwt
payload = jwt.decode(
token,
secret,
algorithms=["HS256"],
audience=["https://api.example.com", "https://admin.example.com"]
)เครื่องมือที่เกี่ยวข้อง
Dmitri is a DevOps engineer who relies on Python as his primary scripting and automation language. He builds internal tooling, CI/CD pipelines, and infrastructure automation scripts that run in production across distributed teams. He writes about the Python standard library, subprocess management, file processing, encoding utilities, and the practical shell-adjacent Python that DevOps engineers use every day.
Maria is a backend developer specialising in Python and API integration. She has broad experience with data pipelines, serialisation formats, and building reliable server-side services. She is an active member of the Python community and enjoys writing practical, example-driven guides that help developers solve real problems without unnecessary theory.