JWT Decoder Python — دليل فك تشفير JWT باستخدام PyJWT
استخدم فك تشفير JWT المجاني مباشرةً في متصفحك — لا حاجة للتثبيت.
جرّب فك تشفير JWT أونلاين ←كل واجهة برمجية تعتمد على المصادقة بالرموز ستسلِّمك في مرحلة ما JWT، ومعرفة ما بداخله من أكثر المهام تكرارًا في التطوير. يأخذ محلِّل JWT في Python تلك السلسلة المُشفَّرة بـ base64 ويحوِّلها إلى قاموس ادعاءات مقروء يمكنك العمل به فعليًا. الحزمة التي تحتاجها على PyPI هي PyJWT — تُثبَّت بـ pip install PyJWT لكنها تُستورَد بـ import jwt. يستعرض هذا الدليل jwt.decode() مع التحقق الكامل من التوقيع، وفك التشفير دون مفتاح سري للفحص السريع، وفك التشفير اليدوي بـ base64 دون أي مكتبة، والتحقق من مفتاح RS256 العام، إضافةً إلى الأخطاء الشائعة التي واجهتها في أنظمة مصادقة الإنتاج. للفحص السريع دون كتابة كود، محلِّل JWT الإلكتروني يعطيك النتيجة فورًا. جميع الأمثلة تستهدف Python 3.10+ و PyJWT 2.x.
- ✓pip install PyJWT، ثم import jwt — اسم الحزمة واسم الاستيراد مختلفان، وهذا يُربك الجميع تقريبًا.
- ✓jwt.decode(token, key, algorithms=["HS256"]) يُعيد قاموسًا بسيطًا يحمل الادعاءات. مرِّر algorithms دائمًا بشكل صريح.
- ✓لفحص الادعاءات دون تحقق: jwt.decode(token, options={"verify_signature": False}, algorithms=["HS256"]).
- ✓لرموز RSA/EC: pip install PyJWT cryptography — الواجهة الخلفية cryptography مطلوبة للخوارزميات غير المتماثلة.
- ✓فك التشفير اليدوي (base64 + json) يعمل دون أي مكتبة لكنه يتخطى التحقق من التوقيع والانتهاء.
ما هو فك تشفير JWT؟
رمز JSON Web Token هو ثلاثة أجزاء مُشفَّرة بـ base64url مفصولة بنقاط: رأس (الخوارزمية ونوع الرمز)، وحمولة (الادعاءات — معرِّف المستخدم، الأدوار، وقت الانتهاء)، وتوقيع. فك تشفير JWT يعني استخراج الجزأين الأول والثاني وفك تشفيرهما بـ base64url وتحليل JSON الناتج إلى قاموس من الادعاءات.
يخبرك الرأس بالخوارزمية المستخدمة لتوقيع الرمز، وأحيانًا يحمل kid (معرِّف المفتاح) للعثور على مفتاح التحقق المناسب. تحمل الحمولة البيانات الفعلية: لمن صدر الرمز (sub)، متى ينتهي (exp)، أي خدمة مُوجَّه إليها (aud)، إضافةً إلى أي ادعاءات مخصصة يُعرِّفها تطبيقك. يُثبت جزء التوقيع أن الرمز لم يُعبَث به، لكنك تحتاج المفتاح السري أو العام للتحقق منه. فك التشفير والتحقق عمليتان منفصلتان. يمكنك فك تشفير الحمولة دون التحقق من التوقيع (مفيد للتصحيح)، لكن لا تعتمد أبدًا على ادعاءات غير موثَّقة في قرارات التفويض.
{
"sub": "usr_8f2a",
"role": "admin",
"exp": 1711815600
}eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c3JfOGYyYSIsInJvbGUiOiJhZG1pbiIsImV4cCI6MTcxMTgxNTYwMH0.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
jwt.decode() — فك التشفير والتحقق باستخدام PyJWT
jwt.decode() هي الدالة الرئيسية من مكتبة PyJWT. تأخذ سلسلة الرمز المُشفَّرة، والمفتاح السري (لخوارزميات HMAC) أو المفتاح العام (لـ RSA/EC)، وقائمة algorithms الإلزامية. تتحقق الدالة من التوقيع، وتفحص الادعاءات القياسية مثل exp و nbf، وتُعيد الحمولة كقاموس Python. إذا فشل أي شيء — توقيع خاطئ، رمز منتهي الصلاحية، خوارزمية خاطئة — يُحدث استثناءً محددًا.
مثال عملي مبسَّط
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 قائمة وليس سلسلة واحدة، وهو إلزامي في PyJWT 2.x. هذه ميزة أمنية: بدونها يستطيع المهاجم صياغة رمز بـ alg: none في الرأس وتجاوز التحقق كليًا. حدِّد دائمًا الخوارزميات التي يقبلها تطبيقك بالضبط. إذا كنت تُصدر رموز HS256 فقط، يجب أن تكون القائمة ["HS256"] — لا ["HS256", "RS256", "none"]. إبقاء القائمة محدودة يقلِّص سطح الهجوم.
شيء أربكني في البداية: غيَّر PyJWT 2.x jwt.encode() ليُعيد سلسلة نصية بدلًا من bytes. إذا كنت تقرأ إجابات قديمة على Stack Overflow تستدعي .decode("utf-8") على الرمز المُشفَّر، فذلك الكود من حقبة PyJWT 1.x وسيُحدث AttributeError على الإصدار 2.x. الرمز سلسلة نصية بالفعل — استخدمه مباشرةً.
دورة كاملة مع تاريخ الانتهاء
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 تلقائيًا أثناء التشفير. أثناء فك التشفير، تعود الادعاءات exp و iat و nbf كأعداد صحيحة، لا كائنات datetime. تحتاج إلى تحويلها بنفسك عبر datetime.fromtimestamp(payload["exp"], tz=timezone.utc).فك تشفير JWT دون التحقق من التوقيع
أحيانًا تحتاج قراءة الادعاءات قبل التمكن من التحقق من الرمز. سيناريو شائع: يحمل رأس الرمز حقل kid (معرِّف المفتاح)، وتحتاج جلب المفتاح العام المطابق من نقطة JWKS قبل التحقق. يدعم 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() يقرأ الرأس فقط — الجزء الأول. استدعاء jwt.decode() مع verify_signature: False يقرأ الحمولة (الجزء الثاني). باستخدام الاثنين معًا يمكنك استخراج كل شيء من الرمز دون مفتاح. لا يزال PyJWT يتحقق من أن الرمز يملك البنية الصحيحة (ثلاثة أجزاء مفصولة بنقاط، base64 صالح، JSON صالح) حتى عند إيقاف التحقق من التوقيع. إذا كانت بنية الرمز مشوهة، يُحدث DecodeError بصرف النظر عن الخيارات التي تمررها.
مرجع معاملات jwt.decode()
التوقيع الكامل هو jwt.decode(jwt, key, algorithms, options, audience, issuer, leeway, require). جميع المعاملات بعد algorithms تُمرَّر بالاسم فقط.
يمنح قاموس options تحكمًا دقيقًا في عمليات التحقق التي يُجريها PyJWT. تُقابل المفاتيح فحوصات فردية: verify_signature، verify_exp، verify_nbf، verify_iss، verify_aud، و verify_iat. جميعها افتراضيًا True إلا عند ضبطها صراحةً على False. في الإنتاج، اترك جميعها على قيمها الافتراضية. الوقت الوحيد الذي أُعطِّل فيه فحوصات فردية هو أثناء التطوير عند العمل مع رموز اختبار قديمة وأحتاج تجاوز الانتهاء مؤقتًا.
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 باستخدام المكتبة القياسية لـ Python فقط — دون pip install على الإطلاق. هذا مفيد فعلًا في عدة حالات: نصوص تصحيح الأخطاء حيث إضافة اعتماد تعدٍّ مبالغ فيه، بيئات CI المقيدة التي تتوفر فيها المكتبة القياسية فقط، دوال AWS Lambda حيث تريد تقليل وقت الإقلاع البارد، أو ببساطة لفهم ما يحدث داخل JWT. الخطوات بسيطة: قسِّم عند النقاط، خذ الجزء المطلوب، أضف حشو base64، فك التشفير، وحلِّل JSON.
وحدتا base64 و json كلتاهما في المكتبة القياسية لـ Python، لذا يعمل هذا الأسلوب على أي تثبيت من Python 3.6 فما فوق. تعالج الدوال أدناه الرأس (الجزء الأول) والحمولة (الجزء الثاني) كلًا على حدة:
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حيلة الحشو (+= "=" * (-len(s) % 4)) هي الجزء الذي ينساه الجميع. يحذف base64url في JWT أحرف = اللاحقة، لكن urlsafe_b64decode في Python تتطلبها. بدون إصلاح الحشو ستحصل على binascii.Error: Incorrect padding.
jwt.decode() مع مفتاح حقيقي.فك تشفير JWT من استجابات API وملفات الرموز
السيناريوهان الأكثر شيوعًا في الواقع: استخراج JWT من استجابة HTTP (نقطة رمز OAuth، واجهة تسجيل الدخول)، وقراءة الرموز من ملفات (بيانات اعتماد حسابات الخدمة، أسرار Kubernetes المُوصَّلة، رموز مؤقتة مخزَّنة على القرص). كلاهما يتطلب معالجة أخطاء مناسبة. طلبات الشبكة تفشل. الملفات تختفي. الرموز تنتهي بين وقت تخزينها ووقت قراءتها.
تستخدم الأمثلة أدناه httpx لطلبات HTTP (يمكن استبداله بـ requests إذا فضَّلت ذلك، النمط متطابق) و pathlib.Path لعمليات الملفات. كل مثال يمسك استثناءات PyJWT المحددة بدلًا من except Exception العام، لتتمكن من الاستجابة بشكل مناسب لكل نمط فشل: إعادة المصادقة عند الانتهاء، التنبيه عند فشل التوقيع، إعادة المحاولة عند مهلة الشبكة.
فك تشفير JWT من استجابة API
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 من سطر الأوامر
أحيانًا تحتاج فقط لإلقاء نظرة على رمز من الطرفية دون كتابة نص برمجي. ربما تُصحِّح تدفق OAuth وتريد رؤية ما في رأس Authorization، أو أخذت رمزًا من أدوات المطور في المتصفح وتريد فحص تاريخ انتهائه. علامة -c في Python تجعل هذا سطرًا واحدًا. مرِّر الرمز واحصل على الادعاءات بصيغة JSON منسَّقة. لا حاجة لملف نص برمجي ولا بيئة افتراضية.
# 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"كبديل مرئي دون أي إعداد للطرفية، الصق رمزك في محلِّل JWT من ToolDeck وشاهد الرأس والحمولة وحالة التحقق من التوقيع فورًا.
python-jose والبدائل الأخرى
python-jose مكتبة JWT بديلة تدعم JWS و JWE (الرموز المُشفَّرة) و JWK بشكل أصلي. إذا احتاج تطبيقك التعامل مع رموز JWT المُشفَّرة (JWE) — حيث الحمولة ذاتها مُشفَّرة وليست مُوقَّعة فقط — فـ python-jose هو الاختيار المناسب لأن PyJWT لا يدعم JWE على الإطلاق. توفر المكتبة أيضًا دعمًا مدمجًا لمجموعات مفاتيح JWKS، مما يُبسِّط التكامل مع موفري الهوية كـ Auth0 و Okta و Keycloak التي تعرض مجموعات مفاتيح متناوبة. واجهة فك التشفير شبه متطابقة مع 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% من حالات JWT، لديه أكبر مجتمع، وواجهته واضحة. انتقل إلى python-jose إذا احتجت دعم JWE أو فضَّلت طريقة تعامله مع JWKS. خيار ثالث يستحق الذكر هو Authlib، الذي يُدمج معالجة JWT داخل إطار أكبر لـ OAuth/OIDC. إذا كنت تستخدم Authlib بالفعل لتدفقات OAuth، فوحدة authlib.jose.jwt تُغنيك عن إضافة اعتماد JWT ثانٍ. وإلا فهو اعتماد ثقيل لمجرد فك تشفير الرموز.
إخراج الطرفية مع تلوين الصيغة
قراءة ادعاءات JWT الخام في الطرفية كافية للفحص السريع، لكن عند تصحيح حمولات الرموز بانتظام (فعلت هذا يوميًا أثناء بناء بوابة مصادقة داخلية)، الإخراج الملوَّن يُحدث فرقًا حقيقيًا. القيم النصية والأرقام والقيم المنطقية و null تظهر بألوان مختلفة، مما يُمكِّنك من اكتشاف صلاحية مفقودة أو طابع زمني خاطئ للانتهاء بنظرة واحدة دون قراءة كل حرف.
مكتبة rich (pip install rich) تملك دالة print_json تأخذ إما سلسلة JSON أو قاموس Python وتطبعه مع تلوين كامل للصيغة في الطرفية. ادمجها مع PyJWT لفحص 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. لا تكتبه إلى ملفات أو تُعيده من نقاط API — هو للعرض في الطرفية فقط. استخدم json.dumps() عند الحاجة إلى إخراج نص عادي.العمل مع دُفعات كبيرة من الرموز
رموز JWT ذاتها صغيرة (أقل من 2 كيلوبايت عادةً)، لكن هناك سيناريوهات تُعالجها بالجملة. تحليل سجلات التدقيق بعد حادث أمني. نصوص ترحيل الجلسات عند تغيير موفري المصادقة. التحقق الدُفعي للامتثال الذي تحتاج فيه إثبات أن كل رمز صدر خلال الـ 90 يومًا الأخيرة وُقِّع بالمفتاح الصحيح. إذا كان لديك عشرات الآلاف من الرموز في ملف سجل NDJSON، معالجتها سطرًا بسطر يتجنب تحميل الملف بأكمله في الذاكرة ويتيح الإبلاغ عن النتائج تدريجيًا.
التحقق الدُفعي من رموز سجل التدقيق
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 للرموز
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 لتوزيع التحقق على أنوية متعددة، إذ كل رمز مستقل.الأخطاء الشائعة
هذه الأخطاء الأربعة تتكرر في مراجعات الكود وأسئلة Stack Overflow. كل واحد منها سهل الوقوع فيه، ورسالة الخطأ التي يُقدِّمها PyJWT لا تشير دائمًا مباشرةً إلى السبب. رأيت مشكلة تسمية الحزمة وحدها تضيع ساعات من وقت التصحيح عندما يثبِّت شخص ما المكتبة الخاطئة ويحصل على واجهة مختلفة تمامًا.
المشكلة: تثبيت pip install jwt أو pip install python-jwt يُثبِّت حزمة مختلفة كليًا. import jwt بعد ذلك يفشل أو يمنحك واجهة لا تعرفها.
الحل: ثبِّت دائمًا بـ pip install PyJWT. الاستيراد هو import jwt. تحقق بـ pip show PyJWT للتأكد من الحزمة الصحيحة.
pip install PyJWT import jwt # correct — this is PyJWT print(jwt.__version__) # 2.x
pip install jwt # or pip install python-jwt import jwt # wrong package — different API entirely
المشكلة: في PyJWT 1.x، كان algorithms اختياريًا ويسمح بأي خوارزمية افتراضيًا. أوجد هذا ثغرة أمنية تتيح للمهاجم ضبط alg: none. PyJWT 2.x الآن يُحدث DecodeError إذا غاب algorithms.
الحل: مرِّر algorithms دائمًا كقائمة صريحة. استخدم فقط الخوارزميات التي يُصدر تطبيقك رموزًا بها فعلًا.
payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
# PyJWT 2.x — this raises DecodeError payload = jwt.decode(token, SECRET_KEY) # DecodeError: algorithms must be specified
المشكلة: تمرير مفتاح سري نصي إلى jwt.decode() مع algorithms=["RS256"] يُحدث InvalidSignatureError. يتطلب RS256 مفتاحًا عامًا بصيغة PEM، لا سلسلة سر مشتركة.
الحل: حمِّل المفتاح العام PEM من ملف أو متغير بيئة. ثبِّت حزمة cryptography: pip install PyJWT cryptography.
public_key = open("public_key.pem").read()
payload = jwt.decode(token, public_key, algorithms=["RS256"])# This fails — RS256 needs a public key, not a string secret payload = jwt.decode(token, "my-secret", algorithms=["RS256"]) # InvalidSignatureError
المشكلة: ترميز base64url في JWT يحذف أحرف = اللاحقة. base64.urlsafe_b64decode في Python يُحدث binascii.Error: Incorrect padding إذا مررت الجزء الخام دون إصلاح الحشو.
الحل: أضف الحشو قبل فك التشفير: segment += '=' * (-len(segment) % 4). هذه الصيغة تُنتج دائمًا العدد الصحيح من أحرف الحشو (0 أو 1 أو 2 أو 3).
import base64
payload_b64 = token.split(".")[1]
payload_b64 += "=" * (-len(payload_b64) % 4)
data = base64.urlsafe_b64decode(payload_b64) # worksimport base64
payload_b64 = token.split(".")[1]
data = base64.urlsafe_b64decode(payload_b64)
# binascii.Error: Incorrect paddingPyJWT مقابل البدائل — مقارنة سريعة
PyJWT هو نقطة البداية المناسبة لمعظم تطبيقات Python. يغطي HMAC و(مع الواجهة الخلفية cryptography) RSA و EC للتحقق من التوقيعات. إذا احتجت JWE (الرموز المُشفَّرة)، انتقل إلى python-jose أو Authlib. فك التشفير اليدوي بـ base64 مناسب للتصحيح لكنه لا يُقدِّم أي ضمانات أمان.
إليك متى أختار كل خيار: PyJWT لأي خدمة ويب قياسية تُجري التحقق بـ HS256 أو RS256. python-jose عندما تشمل البنية رموزًا مُشفَّرة أو تناوب JWKS. فك التشفير اليدوي بـ base64 للفحص السريع في بيئات لا تتوفر فيها pip (حاويات CI، مضيفات إنتاج مقيدة، إقلاعات AWS Lambda الباردة التي تريد تقليل الاعتماديات). Authlib عندما يستخدمه المشروع بالفعل لتدفقات OAuth وإضافة مكتبة JWT ثانية ستكون تكرارًا.
كبديل بدون كود، الصق أي رمز في محلِّل JWT لرؤية الرأس والحمولة المفككَين مع ملاحظات التحقق من الادعاءات.
الأسئلة الشائعة
كيف أفك تشفير JWT في Python دون التحقق من التوقيع؟
مرِّر options={"verify_signature": False} و algorithms=["HS256"] إلى jwt.decode(). يُعيد هذا قاموس الحمولة دون فحص التوقيع. استخدم هذا الأسلوب فقط للفحص — قراءة الادعاءات قبل جلب المفتاح العام المناسب، أو تصحيح الأخطاء في بيئة التطوير. لا تتخطَّ التحقق أبدًا على الرموز التي تتحكم في الوصول إلى أي شيء.
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) عن اسم الاستيراد (jwt). التزم بـ PyJWT ما لم يكن لديك سبب محدد للتغيير.
كيف أفك تشفير JWT باستخدام RS256 في Python؟
ثبِّت PyJWT مع الواجهة الخلفية cryptography: 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 (انتهاء الصلاحية) تلقائيًا. إذا تجاوز وقت UTC الحالي الطابع الزمني exp، يُحدث jwt.ExpiredSignatureError. يمكنك إضافة تسامح مع انحراف الساعة عبر المعامل leeway: jwt.decode(token, key, algorithms=["HS256"], leeway=timedelta(seconds=30)). يمنح هذا فترة سماح 30 ثانية. لتعطيل فحص الانتهاء كليًا (غير مستحسن في الإنتاج)، مرِّر 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؟
نعم. قسِّم الرمز عند النقاط، خذ الجزء الثاني (الحمولة)، أضف أحرف = حتى يصبح طوله مضاعفًا للأربعة، ثم فك تشفيره بـ base64url وحلِّل 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. يمكنك أيضًا تمرير قائمة من الجماهير المقبولة إذا كانت خدمتك تقبل رموزًا مُوجَّهة لعدة واجهات برمجية.
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.