JWT Decoder Python — دليل فك تشفير JWT باستخدام PyJWT

·DevOps Engineer & Python Automation Specialist·مراجعة بواسطةMaria Santos·نُشر

استخدم فك تشفير 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)، إضافةً إلى أي ادعاءات مخصصة يُعرِّفها تطبيقك. يُثبت جزء التوقيع أن الرمز لم يُعبَث به، لكنك تحتاج المفتاح السري أو العام للتحقق منه. فك التشفير والتحقق عمليتان منفصلتان. يمكنك فك تشفير الحمولة دون التحقق من التوقيع (مفيد للتصحيح)، لكن لا تعتمد أبدًا على ادعاءات غير موثَّقة في قرارات التفويض.

After · json
Before · json
{
  "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. إذا فشل أي شيء — توقيع خاطئ، رمز منتهي الصلاحية، خوارزمية خاطئة — يُحدث استثناءً محددًا.

مثال عملي مبسَّط

Python 3.10+
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. الرمز سلسلة نصية بالفعل — استخدمه مباشرةً.

دورة كاملة مع تاريخ الانتهاء

Python 3.10+ — encode then decode with exp
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")
ملاحظة:يُحوِّل PyJWT كائنات datetime إلى طوابع زمنية Unix تلقائيًا أثناء التشفير. أثناء فك التشفير، تعود الادعاءات exp و iat و nbf كأعداد صحيحة، لا كائنات datetime. تحتاج إلى تحويلها بنفسك عبر datetime.fromtimestamp(payload["exp"], tz=timezone.utc).

فك تشفير JWT دون التحقق من التوقيع

أحيانًا تحتاج قراءة الادعاءات قبل التمكن من التحقق من الرمز. سيناريو شائع: يحمل رأس الرمز حقل kid (معرِّف المفتاح)، وتحتاج جلب المفتاح العام المطابق من نقطة JWKS قبل التحقق. يدعم PyJWT هذا عبر خيار verify_signature: False. لا تزال تمرِّر قائمة algorithms، لكن المعامل key يُتجاهَل.

Python 3.10+ — unverified decode
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 تُمرَّر بالاسم فقط.

المعامل
النوع
القيمة الافتراضية
الوصف
jwt
str | bytes
(مطلوب)
سلسلة JWT المُشفَّرة المراد فك تشفيرها
key
str | bytes | dict
(مطلوب)
المفتاح السري (HMAC) أو المفتاح العام (RSA/EC) للتحقق
algorithms
list[str]
(مطلوب)
الخوارزميات المسموح بها — مثل ["HS256"]، ["RS256"]. لا تحذف هذا المعامل أبدًا.
options
dict
{}
تجاوز علامات التحقق: verify_signature، verify_exp، verify_aud، إلخ.
audience
str | list[str]
None
قيمة aud المتوقعة — يرفع InvalidAudienceError عند عدم التطابق
issuer
str
None
قيمة iss المتوقعة — يرفع InvalidIssuerError عند عدم التطابق
leeway
timedelta | int
0
ثوانٍ من التسامح مع انحراف الساعة لفحوصات exp و nbf
require
list[str]
[]
الادعاءات الواجب توافرها — يرفع MissingRequiredClaimError إذا غابت

يمنح قاموس options تحكمًا دقيقًا في عمليات التحقق التي يُجريها PyJWT. تُقابل المفاتيح فحوصات فردية: verify_signature، verify_exp، verify_nbf، verify_iss، verify_aud، و verify_iat. جميعها افتراضيًا True إلا عند ضبطها صراحةً على False. في الإنتاج، اترك جميعها على قيمها الافتراضية. الوقت الوحيد الذي أُعطِّل فيه فحوصات فردية هو أثناء التطوير عند العمل مع رموز اختبار قديمة وأحتاج تجاوز الانتهاء مؤقتًا.

Python 3.10+ — using options and require
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 فما فوق. تعالج الدوال أدناه الرأس (الجزء الأول) والحمولة (الجزء الثاني) كلًا على حدة:

Python 3.10+ — manual JWT decode without any library
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 وإعادة تشفيرها. استخدم هذا الأسلوب للفحص وتصحيح الأخطاء فقط، وليس لمنطق التفويض. لأي شيء مهم، استخدم jwt.decode() مع مفتاح حقيقي.

فك تشفير JWT من استجابات API وملفات الرموز

السيناريوهان الأكثر شيوعًا في الواقع: استخراج JWT من استجابة HTTP (نقطة رمز OAuth، واجهة تسجيل الدخول)، وقراءة الرموز من ملفات (بيانات اعتماد حسابات الخدمة، أسرار Kubernetes المُوصَّلة، رموز مؤقتة مخزَّنة على القرص). كلاهما يتطلب معالجة أخطاء مناسبة. طلبات الشبكة تفشل. الملفات تختفي. الرموز تنتهي بين وقت تخزينها ووقت قراءتها.

تستخدم الأمثلة أدناه httpx لطلبات HTTP (يمكن استبداله بـ requests إذا فضَّلت ذلك، النمط متطابق) و pathlib.Path لعمليات الملفات. كل مثال يمسك استثناءات PyJWT المحددة بدلًا من except Exception العام، لتتمكن من الاستجابة بشكل مناسب لكل نمط فشل: إعادة المصادقة عند الانتهاء، التنبيه عند فشل التوقيع، إعادة المحاولة عند مهلة الشبكة.

فك تشفير JWT من استجابة API

Python 3.10+ — decode JWT from OAuth token endpoint
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 من ملف

Python 3.10+ — read and decode a cached token file
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 منسَّقة. لا حاجة لملف نص برمجي ولا بيئة افتراضية.

Bash
# 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"
# }
Bash
# 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"
# }
Bash
# 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، لذا التبديل بينهما يتطلب تغييرات بسيطة:

Python 3.10+ — python-jose
# 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 في سطرين:

Python 3.10+ — rich terminal output
# 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، معالجتها سطرًا بسطر يتجنب تحميل الملف بأكمله في الذاكرة ويتيح الإبلاغ عن النتائج تدريجيًا.

التحقق الدُفعي من رموز سجل التدقيق

Python 3.10+ — streaming token validation
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 للرموز

Python 3.10+ — extract and transform claims from token log
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 لا تشير دائمًا مباشرةً إلى السبب. رأيت مشكلة تسمية الحزمة وحدها تضيع ساعات من وقت التصحيح عندما يثبِّت شخص ما المكتبة الخاطئة ويحصل على واجهة مختلفة تمامًا.

الخلط بين أسماء حزمتَي PyJWT و python-jwt

المشكلة: تثبيت pip install jwt أو pip install python-jwt يُثبِّت حزمة مختلفة كليًا. import jwt بعد ذلك يفشل أو يمنحك واجهة لا تعرفها.

الحل: ثبِّت دائمًا بـ pip install PyJWT. الاستيراد هو import jwt. تحقق بـ pip show PyJWT للتأكد من الحزمة الصحيحة.

After · Python
Before · Python
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
حذف معامل algorithms

المشكلة: في PyJWT 1.x، كان algorithms اختياريًا ويسمح بأي خوارزمية افتراضيًا. أوجد هذا ثغرة أمنية تتيح للمهاجم ضبط alg: none. PyJWT 2.x الآن يُحدث DecodeError إذا غاب algorithms.

الحل: مرِّر algorithms دائمًا كقائمة صريحة. استخدم فقط الخوارزميات التي يُصدر تطبيقك رموزًا بها فعلًا.

After · Python
Before · Python
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() بنوع مفتاح خاطئ لـ RS256

المشكلة: تمرير مفتاح سري نصي إلى jwt.decode() مع algorithms=["RS256"] يُحدث InvalidSignatureError. يتطلب RS256 مفتاحًا عامًا بصيغة PEM، لا سلسلة سر مشتركة.

الحل: حمِّل المفتاح العام PEM من ملف أو متغير بيئة. ثبِّت حزمة cryptography: pip install PyJWT cryptography.

After · Python
Before · Python
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
نسيان حشو base64 عند فك التشفير يدويًا

المشكلة: ترميز base64url في JWT يحذف أحرف = اللاحقة. base64.urlsafe_b64decode في Python يُحدث binascii.Error: Incorrect padding إذا مررت الجزء الخام دون إصلاح الحشو.

الحل: أضف الحشو قبل فك التشفير: segment += '=' * (-len(segment) % 4). هذه الصيغة تُنتج دائمًا العدد الصحيح من أحرف الحشو (0 أو 1 أو 2 أو 3).

After · Python
Before · Python
import base64
payload_b64 = token.split(".")[1]
payload_b64 += "=" * (-len(payload_b64) % 4)
data = base64.urlsafe_b64decode(payload_b64)  # works
import base64
payload_b64 = token.split(".")[1]
data = base64.urlsafe_b64decode(payload_b64)
# binascii.Error: Incorrect padding

PyJWT مقابل البدائل — مقارنة سريعة

الأسلوب
يتحقق من التوقيع
يتحقق من الادعاءات
أنواع مخصصة
يتطلب تثبيتًا
PyJWT jwt.decode()
✓ (exp, aud, iss, nbf)
لا ينطبق (يُعيد dict)
pip install PyJWT
PyJWT فك تشفير غير موثَّق
لا ينطبق
pip install PyJWT
فك تشفير base64 يدوي
لا ينطبق
لا (مكتبة قياسية)
python-jose jwt.decode()
لا ينطبق
pip install python-jose
Authlib jwt.decode()
لا ينطبق
pip install Authlib
PyJWT + cryptography
✓ (RSA/EC)
لا ينطبق
pip install PyJWT cryptography

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(). يُعيد هذا قاموس الحمولة دون فحص التوقيع. استخدم هذا الأسلوب فقط للفحص — قراءة الادعاءات قبل جلب المفتاح العام المناسب، أو تصحيح الأخطاء في بيئة التطوير. لا تتخطَّ التحقق أبدًا على الرموز التي تتحكم في الوصول إلى أي شيء.

Python
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.

Python
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}.

Python
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، أو لنصوص تصحيح الأخطاء السريعة.

Python
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. يمكنك أيضًا تمرير قائمة من الجماهير المقبولة إذا كانت خدمتك تقبل رموزًا مُوجَّهة لعدة واجهات برمجية.

Python
import jwt

payload = jwt.decode(
    token,
    secret,
    algorithms=["HS256"],
    audience=["https://api.example.com", "https://admin.example.com"]
)

الأدوات ذات الصلة

DV
Dmitri VolkovDevOps Engineer & Python Automation Specialist

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.

MS
Maria Santosالمراجع التقني

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.