จะใช้ pyJWT เพื่อตรวจสอบลายเซ็นใน JWT นี้ได้อย่างไร

ฉันได้รับโทเค็น JWT ฉันสนใจที่จะแกะมันและตรวจสอบความถูกต้องของเนื้อหา

ฉันต้องการใช้ไลบรารี pyJWT เพื่อทำสิ่งนี้ ดูโปรแกรมขนาดเล็กด้านล่าง

ฉันถอดรหัส base64 JWT ของฉันสำเร็จแล้ว และฉันได้ดึงข้อมูล JWK ที่ตรงกันจากเว็บแล้ว แต่ฉันไม่รู้ว่าจะต้องทำอย่างไรต่อไปเพื่อตรวจสอบลายเซ็น

เมื่อฉันพยายามทำ jwt.decode() ด้านล่าง ฉันได้รับข้อผิดพลาด jwt.exceptions.DecodeError: Not enough segments

# !/usr/bin/env python

import jwt
import base64
import json
import requests

my_jwt = "eyJqa3UiOiJodHRwczovL2U5N2I4YTlkNjcyZTRjZTQ4NDVlYzY5NDdjZDY2ZW" \
         "Y2LXNiLmJhYXMubmludGVuZG8uY29tLzEuMC4wL2NlcnRpZmljYXRlcyIsImtp" \
         "ZCI6ImZlOWRiYmZmLTQ3MGItNDZjOC04YmFmLTFiNzY5OGRlZTViZSIsImFsZy" \
         "I6IlJTMjU2In0.eyJpc3MiOiJodHRwczovL2U5N2I4YTlkNjcyZTRjZTQ4NDVl" \
         "YzY5NDdjZDY2ZWY2LXNiLmJhYXMubmludGVuZG8uY29tIiwiZXhwIjoxNTQ1MT" \
         "g1NDk2LCJ0eXAiOiJpZF90b2tlbiIsImF1ZCI6IjhkOTc1NTllNjNlY2NkNTYi" \
         "LCJiczpkaWQiOiI2NjJhZTQwOWYwNTQyYTBjIiwic3ViIjoiOTNkYmYwNDdiYT" \
         "I3NzQ5NSIsImp0aSI6IjY1NDg4ZjJmLTI1NzAtNDBkYy04ODQ3LTMzODNlZWIx" \
         "MGJiYiIsIm5pbnRlbmRvIjp7ImFpIjoiMDEwMGY4MDAwMDQ5MjAwMCIsImF2Ij" \
         "oiMDAwMCIsImVkaSI6ImJjNTdiYmM3MTZlMDA1MGFmOWRhN2NkYTIzMWRjZDgy" \
         "IiwiYXQiOjE1NDUxNzQ2OTZ9LCJpYXQiOjE1NDUxNzQ2OTZ9.ZMUIt3wYrbfhX" \
         "nnDh4WraGlKrZy0YuL5prluY70sU_-0W5XvWIB-xmTrLz7LJWHEGwTskcWf81_" \
         "HBq_mSb75rMfTAEBwBmOJ4ITmhdnXksz8w7EDOWuPPSEft5XLMNOMD16ztEOYe" \
         "5ddU_iqNEbT56L7fcAJEXv0FWy6H_OutxOglYpDaNkcj6CWJ7dpA0JbqerR9dE" \
         "szaLwyn1ZBDPVD0YeAIm5bEr61imeedzMb0amxlTl4R87mqK6epsFUnRy6p6Kl" \
         "r27_DlTLQ-gej09W7NeNzONCj4thHgCr9szAiaN28krfTc2fobz3qFCoC_eQgh" \
         "iIIZBe_-Lksng3Eg6tw"

decoded_token = [
    base64.b64decode(i + '=' * (-len(i) % 4))
    for i in my_jwt.split('.')
]
header = json.loads(decoded_token[0])
claims = json.loads(decoded_token[1])
signature = decoded_token[2]

print 'Header = \n{}\n\n'.format(header)
print 'Claims = \n{}\n\n'.format(claims)
print 'Signature = \n{}\n\n'.format(signature)

key_found = False
for curr_jwk in requests.get(header['jku']).json()['keys']:
    if curr_jwk['kid'] == header['kid']:
        key_found = True
        break
assert key_found
print 'curr_jwk = {}\n\n'.format(curr_jwk)

# I am not sure if the following line is correct
jwt.decode(signature, curr_jwk['x5c'][0], algorithms=['RS256'])

ด้านล่างนี้เป็นผลลัพธ์แบบเต็ม โปรดแสดงให้ฉันเห็นว่าฉันสามารถยืนยันลายเซ็นได้อย่างไร ฉันไม่พบตัวอย่างหลามบนเว็บที่เหมาะกับฉัน

Header =
{u'jku': u'https://e97b8a9d672e4ce4845ec6947cd66ef6-sb.baas.nintendo.com/1.0.0/certificates', u'alg': u'RS256', u'kid': u'fe9dbbff-470b-46c8-8baf-1b7698dee5be'}


Claims =
{u'aud': u'8d97559e63eccd56', u'iss': u'https://e97b8a9d672e4ce4845ec6947cd66ef6-sb.baas.nintendo.com', u'jti': u'65488f2f-2570-40dc-8847-3383eeb10bbb', u'exp': 1545185496, u'nintendo': {u'ai': u'0100f80000492000', u'edi': u'bc57bbc716e0050af9da7cda231dcd82', u'at': 1545174696, u'av': u'0000'}, u'iat': 1545174696, u'bs:did': u'662ae409f0542a0c', u'typ': u'id_token', u'sub': u'93dbf047ba277495'}


Signature =
�dr>�X�ݤn��G�D�6��)�d�T=x&�+�X�y�s1��S��|�j���lI�˪z*Z��9S�OV�׍��B��a������v�J�M͟���P�m>z/� �d�����H:�)


curr_jwk = {
    u'use': u'sig', 
    u'e': u'AQAB', 
    u'kty': u'RSA', 
    u'alg': u'RS256', 
    u'n': u'twZRFUFinbzSF9aCaLqSdDSfS4Lfz6bSB-4ymwqkgJPLMLtX0meEVOSsx2qP_OQFkb6_RAZk3GK2CSM46V8A-bZqYn1jHyRyQeQmAfrzvXxAiXBdSq4Eso1C_roFQDjR3p-RnLWDLdMPUHkmAQSPEpVAUbcCvUD8zZwQ-go8GAzG0iQNNvZxma1JQoDsHPgY5LiBVBlqY43tp_jeW_sb3SxZ30zzIckKtQfYDfph_vns-q1_raNRPgS0TQw4E470n27UgLE30yoVn_DlYwmtZneCOWIByVPrMBMIkrv_Gh9VW3GszbqZz2u3K9Aj7rtnCtmUrI9dQbeTnruWOn_Fjw', 
    u'x5c': [u'MIIC/zCCAeegAwIBAgIHBX0u6g3YCzANBgkqhkiG9w0BAQUFADBAMT4wPAYDVQQDEzVlOTdiOGE5ZDY3MmU0Y2U0ODQ1ZWM2OTQ3Y2Q2NmVmNi1zYi5iYWFzLm5pbnRlbmRvLmNvbTAeFw0xNzEyMTYwMDAwMDBaFw0xOTEyMTcwMDAwMDBaMEAxPjA8BgNVBAMTNWU5N2I4YTlkNjcyZTRjZTQ4NDVlYzY5NDdjZDY2ZWY2LXNiLmJhYXMubmludGVuZG8uY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtwZRFUFinbzSF9aCaLqSdDSfS4Lfz6bSB+4ymwqkgJPLMLtX0meEVOSsx2qP/OQFkb6/RAZk3GK2CSM46V8A+bZqYn1jHyRyQeQmAfrzvXxAiXBdSq4Eso1C/roFQDjR3p+RnLWDLdMPUHkmAQSPEpVAUbcCvUD8zZwQ+go8GAzG0iQNNvZxma1JQoDsHPgY5LiBVBlqY43tp/jeW/sb3SxZ30zzIckKtQfYDfph/vns+q1/raNRPgS0TQw4E470n27UgLE30yoVn/DlYwmtZneCOWIByVPrMBMIkrv/Gh9VW3GszbqZz2u3K9Aj7rtnCtmUrI9dQbeTnruWOn/FjwIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQBsXUY5j/m4pAB0R3zDEHHBXZUCkdN53zCr5M7/Kn4ak0vKb9Hk1gNJqdS+dNyW1C7aNGFPmmcMhOos3tv0KsjNoftA4A27uIA7U6QYtJSUVbtVJgYCu8XUa2VDongGNxB72UvFiD++qY3Xe95b0kzsoSOqGaguicAeRSG/HARDaNJT5I9MhCYdCmjU/z83dGI0t8v33Zua92YVmzL6E06lxQ7KrVa9MlKgVJEiAnju3GESVFmrhEoVt516EVxvTVmFlrsvN6rh+c/jC3mBDkE7E23ukg6FpM8oCVRU/5o8/lnKVcRhYPnBgHwg/wTL/Is5rteMtLihV/mvgt3InWkc'],
    u'usage': u'developer', 
    u'kid': u'fe9dbbff-470b-46c8-8baf-1b7698dee5be'
}

Traceback (most recent call last):
  File "./saqib_test.py", line 46, in <module>
    jwt.decode(signature, curr_jwk['x5c'][0], algorithms=['RS256'])
  File "/Users/saqib.ali/saqib-env-987/lib/python2.7/site-packages/jwt/api_jwt.py", line 84, in decode
    payload, _, _, _ = self._load(jwt)
  File "/Users/saqib.ali/saqib-env-987/lib/python2.7/site-packages/jwt/api_jws.py", line 183, in _load
    raise DecodeError('Not enough segments')
jwt.exceptions.DecodeError: Not enough segments

person Saqib Ali    schedule 19.12.2018    source แหล่งที่มา


คำตอบ (2)


ขั้นแรก คุณควรถอดรหัสข้อความที่เข้ารหัส base64 โดยตรง นั่นเป็นข้อความ jwt ทั้งหมด ไม่ใช่แค่ลายเซ็น ซึ่งควรจะเป็น:

jwt.decode(my_jwt, algorithms='RS256')

ประการที่สอง มันจะใช้งานไม่ได้เพราะตามค่าเริ่มต้นไม่มีการรองรับ RS256 คุณสามารถอ้างถึงปัญหานี้: https://github.com/jpadilla/pyjwt/issues/181

สุดท้ายนี้ ฉันไม่คิดว่าคุณจะสามารถถอดรหัสข้อความที่เข้ารหัส RS256 นี้ได้โดยไม่ต้องมีรหัสสาธารณะ คุณมีกุญแจอันนั้นไหม?

person Sraw    schedule 19.12.2018
comment
ฉันไม่แน่ใจว่าฉันมีรหัสสาธารณะหรือไม่ ฉันคิดว่าฉันทำ. แต่ฉันไม่แน่ใจ 100% curr_jwk['x5c'][0] เป็นกุญแจสาธารณะใช่ไหม - person Saqib Ali; 20.12.2018
comment
จริงๆแล้วฉันไม่รู้ คุณสามารถลองได้ หากคุณประสบความสำเร็จก็คือ - person Sraw; 20.12.2018
comment
ความเข้าใจของฉันคือคุณสามารถถอดรหัสโทเค็น RS256 ได้ (ซึ่งเป็นเพียงการเข้ารหัส base64 - ใช้อาร์กิวเมนต์: Verify=False) - แต่คุณไม่สามารถยืนยันได้หากไม่มีรหัสสาธารณะ - person python1981; 06.08.2019

คุณทำให้กระบวนการถอดรหัสของคุณยากมาก

นี่เป็นหนึ่งซับในการถอดรหัสโทเค็น JWT:

JWT_SECRET = 'secretpassphrase'
JWT_ALGORITHM = 'HS256'
decoded = json.loads(json.dumps(jwt.decode(token, JWT_SECRET, JWT_ALGORITHM)))

สิ่งที่บรรทัดนี้ทำคือการถอดรหัสโทเค็นโดยใช้รหัสผ่านลับที่ใช้ในการสร้างโทเค็นนั้น json.dumps จะโหลดโทเค็นเป็นรูปแบบไบต์ ส่วน json.loads ถัดไปจะให้คุณแยกวิเคราะห์โทเค็นด้วยโค้ดด้านล่างนี้

หลังจากนี้ คุณจะได้รับคุณลักษณะเฉพาะของโทเค็นดังนี้:

user_id = decoded['user_id']
expiry_time = decoded['exp']

หมายเหตุ: ควรใส่สิ่งนี้ลงในคำสั่ง Try: Catch เพราะหากโทเค็นหมดอายุหรือไม่ได้ใช้ลายเซ็นเดียวกัน python จะเกิดข้อผิดพลาด ลายเซ็นหรือโทเค็นไม่ถูกต้องหมดอายุแล้ว

def verify_token(token):
    try:
        decoded = json.loads(json.dumps(jwt.decode(token, JWT_SECRET, JWT_ALGORITHM)))
        return {
           'name': decoded['user_id'],
           'exp': decoded['exp']
           }
    except:
        print("Generic Error")

หวังว่านี่จะช่วยได้

person D3vRandom    schedule 26.07.2020
comment
มันช่วยฉันได้มากจริงๆ ขอบคุณมาก ฉันดูปัญหาโทเค็น jwt นี้มาระยะหนึ่งแล้วในฟังก์ชัน Lambda ของฉัน แต่ไม่รู้ว่าต้องทำอย่างไร @ D3vRandom ความคิดเห็นของคุณมาในเวลาที่เหมาะสมสำหรับฉันอย่างแท้จริง - person flyiinhigh; 26.07.2020