JWT असल में क्या है
एक JSON Web Token एक स्ट्रिंग है। पूरा खेल यही है। क्लाइंट को सेशन आईडी देकर सेशन सर्वर पर रखने के बजाय, आप उसे एक हस्ताक्षरित कथन देते हैं — “यह उपयोगकर्ता 4821 है, ये इसके अधिकार हैं, यह 14:05 पर समाप्त होगा” — और वही कथन वह अपने साथ लेकर चलता है। हस्ताक्षर कुंजी पर भरोसा करने वाली कोई भी सेवा उस कथन को पढ़कर, डेटाबेस से पूछे बिना, काम कर सकती है।
इसी एक वजह से JWT बहुत सुविधाजनक और थोड़े ख़तरनाक, दोनों हैं: डेटा क्लाइंट के हाथ में है। आपका जारी किया टोकन जिसके पास हो वह पढ़ सकता है, समाप्ति से पहले उसे वापस नहीं लिया जा सकता, और हस्ताक्षर पर भरोसा करने वाली हर चीज़ उसे स्वीकार कर लेगी। प्रोडक्शन में JWT की लगभग सारी दिक्कतें इन तीन बातों में से किसी एक को भूलने से आती हैं।
टोकन बिंदुओं से अलग किए तीन अटपटे ब्लॉक जैसा दिखता है:
eyJhbGciOiJIUzI1NiIsImtpZCI6ImtleS0yIn0.eyJzdWIiOiI0ODIxIiwiZXhwIjoxNzY1MTExMTExfQ.3xS7_c9Nn-aQ
यह एन्क्रिप्टेड नहीं है। पहले दो ब्लॉक Base64url में सादा JSON हैं — यह URL के लिए सुरक्षित
वर्णमाला है, जो + और / की जगह - और _ रखती है और
अंत का = पैडिंग हटा देती है। कोई भी उन्हें एक सेकंड में फिर JSON बना सकता है — ठीक यही
काम एक JWT डिकोडर करता है।
तीन हिस्से और हर एक का काम
टोकन को बिंदुओं पर तोड़ें और आपके पास हेडर, पेलोड और सिग्नेचर होंगे।
हेडर
छोटा, और लगभग हमेशा कुंजियों के बारे में। alg हस्ताक्षर एल्गोरिदम बताता है,
typ आम तौर पर JWT होता है, और kid एक कुंजी आईडी है जो
जाँचने वाले को बताती है कि जारीकर्ता के पास एक से अधिक कुंजी होने पर कौन-सी सार्वजनिक कुंजी लानी
है। कुंजी बदलने के बाद टोकन अचानक सत्यापित होना बंद कर दे, तो पहले kid देखें।
पेलोड
क्लेम: वही असली कथन जो किया जा रहा है। कुछ क्लेम स्पेसिफ़िकेशन में पंजीकृत हैं
(iss, sub, aud, exp, nbf,
iat, jti) और बाकी वही हैं जो आपके जारीकर्ता ने जोड़ने का फ़ैसला किया:
scope, roles, tenant_id, कोई ईमेल पता, कोई फ़ीचर फ़्लैग।
सिग्नेचर
पहले दो हिस्सों पर एक MAC या डिजिटल हस्ताक्षर — ठीक उसी रूप में जैसे वे स्ट्रिंग में दिखते हैं। यह बारीकी मायने रखती है: हस्ताक्षर एन्कोडेड बाइट्स को ढकता है, इसलिए JSON को फिर से क्रमबद्ध कर दोबारा एन्कोड करने पर ऐसा टोकन बनता है जो सत्यापित नहीं होता, चाहे डेटा हू-ब-हू वही हो। टोकन को “साफ़-सुथरा” करने के लिए कभी दोबारा न बनाएँ।
टोकन कैसे डिकोड करें और पहले क्या देखें
पूरी स्ट्रिंग, बिंदुओं सहित, डिकोडर में पेस्ट करें। अगर आपने उसे किसी रिक्वेस्ट से कॉपी किया है
तो Bearer उपसर्ग हटा दें — या ऐसा टूल लें जो उसे ख़ुद हटा दे। फिर इस क्रम में
पढ़ें:
- डिकोड हुआ ही क्या? अगर डिकोडर कहता है कि हिस्से मान्य Base64url JSON नहीं हैं, तो टोकन कटा या बिगड़ा हुआ पहुँचा है। आकार की सीमा से टकराई कुकी और पंक्तियों में टूटा हेडर, दोनों ऐसे ही दिखते हैं।
exp। “पाँच मिनट पहले तो चल रहा था” की ज़्यादातर शिकायतें समाप्ति से ही निकलती हैं, और सबसे पहले इसे ही ख़ारिज करना चाहिए।audऔरiss। किसी दूसरे प्राप्तकर्ता के लिए बना टोकन पूरी तरह मान्य टोकन है, जिसे अस्वीकार करना आपके API का सही फ़ैसला है।- कस्टम क्लेम। मानक क्लेम सही निकलने के बाद ही इस पर बहस का मतलब है कि
scopeमें वही है या नहीं जिसकी आपको उम्मीद थी।
जहाँ हो सके, स्थानीय रूप से डिकोड करें। टोकन एक जीवित क्रेडेंशियल है, इसलिए उसे किसी भी साइट पर पेस्ट करना वैसी ही गलती है जैसी पासवर्ड पेस्ट करना: काम का हर टूल ब्राउज़र में डिकोड करता है और कहीं कुछ नहीं भेजता।
वे क्लेम जो तय करते हैं कि टोकन चलेगा या नहीं
लगभग सारा काम पाँच क्लेम करते हैं, और इनमें से हर एक अकेले ही रिक्वेस्ट ठुकरा सकता है।
exp— समाप्ति, सेकंड में Unix टाइमस्टैम्प के रूप में। यहाँ मिलीसेकंड का मान एक पुरानी गलती है: वह साल 57000 में जा गिरता है और टोकन कभी समाप्त नहीं होता। अच्छा डिकोडर असली तारीख और यह दोनों दिखाता है कि वह कितने समय पहले बीती।nbf— not before। इस क्षण तक टोकन अमान्य है। जान-बूझकर कम ही इस्तेमाल होता है, कभी-कभी आगे चल रही घड़ी वाली मशीन पर गलती से लग जाता है।iat— जारी होने का समय। तकनीकी रूप से न समाप्त हुए पर संदेहास्पद रूप से पुराने टोकन ठुकराने के लिए उपयोगी, औरiatभविष्य में हो तो घड़ी की दिक्कत पकड़ने के लिए भी।aud— प्राप्तकर्ता, यानी टोकन किसके लिए है। यह जाँच छोड़ देने वाला API आपकी ही दूसरी सेवा द्वारा अपने लिए जारी टोकन खुशी-खुशी स्वीकार कर लेगा, और यह माइक्रोसर्विसों के बीच विशेषाधिकार बढ़ाने का असली रास्ता है।iss— जारीकर्ता। जिस जारीकर्ता की अपेक्षा है उसे ठीक-ठीक तय कर दें, और कुंजियाँ केवल उसी के प्रकाशित कुंजी-सेट से लें।
jti अलग किस्म का है: इस टोकन की अनोखी पहचान। इसका अर्थ तभी है जब आप इसे संग्रहित कर
जाँचें, लेकिन किसी एक टोकन को रद्द कर पाना इसी से संभव होता है।
डिकोड करना सत्यापित करना नहीं है
यही बात लोगों को फँसाती है। डिकोडिंग Base64url पढ़कर आपको JSON दिखाती है। वह बताती है कि टोकन क्या कहता है। वह बिल्कुल नहीं बताती कि वह कथन सच है या नहीं।
कोई भी {"sub":"1","role":"admin"} लिखकर, उसे Base64url में बदलकर, अंत में
कोई भी स्ट्रिंग सिग्नेचर की जगह चिपकाकर आपको दे सकता है। वह किसी भी डिकोडर में, हमारे वाले में भी,
बढ़िया डिकोड होगा। उसे चलने से रोकता है सत्यापन का चरण: जारीकर्ता की कुंजी से सिग्नेचर दोबारा
गणना करना और मेल न खाने पर टोकन ठुकरा देना। उस चरण के लिए कोई सीक्रेट या सार्वजनिक कुंजी चाहिए, वह
आपके सर्वर का काम है, और कोई ब्राउज़र टूल उसे आपके लिए नहीं कर सकता।
इसलिए डिकोडर को एक रीडिंग मानें, सबूत कभी नहीं। “मैंने डिकोड किया और उसमें admin लिखा है” सुरक्षा का निष्कर्ष नहीं है। “मेरे सर्वर ने सत्यापित किया और उसमें admin लिखा है” है।
ठीक दिखने वाले टोकन को 401 क्यों मिलता है
जब टोकन साफ़-साफ़ डिकोड होता है और API फिर भी मना करता है, तो लगभग हमेशा इनमें से कोई एक कारण होता है:
- समाप्त।
expको सर्वर की घड़ी से मिलाएँ, अपनी घड़ी से नहीं। - घड़ी का अंतर। दो मिनट आगे चल रहा सर्वर अभी-अभी जारी हुए टोकन भी ठुकरा देगा। ज़्यादातर लाइब्रेरियाँ इसी वजह से थोड़ी छूट देती हैं; आपकी में वह शून्य हो तो उसे फिर से देखना चाहिए।
- गलत प्राप्तकर्ता। आम तब, जब एक ही आइडेंटिटी प्रोवाइडर कई API को सेवा देता है।
- कुंजी बदलना। जारीकर्ता ने नई कुंजी से हस्ताक्षर किया, जाँचने वाले के कैश में
पुराना कुंजी-सेट पड़ा है, और
kidअब हल नहीं होता। - टोकन पहुँचा ही नहीं। गायब या बिगड़ा
Authorizationहेडर, गिरी हुई कुकी, या हेडर हटा देने वाला प्रॉक्सी। टोकन को दोष देने से पहले देखें कि सर्वर को असल में क्या मिला: HTTP स्टेटस कोड लुकअप और हमारी स्टेटस कोड गाइड 401 को उस 403 से अलग पहचानने में मदद करती हैं जिसका मतलब कुछ और ही होता है।
401 या 403?
401 का मतलब है क्रेडेंशियल गायब है, समाप्त है या सत्यापित नहीं हो सकता: टोकन
ठीक करें। 403 का मतलब है क्रेडेंशियल ठीक था और जवाब फिर भी ना है: अनुमतियाँ ठीक करें,
या scope क्लेम। अधिकार-विफलता पर 401 लौटाने वाली सेवाएँ लोगों को उस टोकन की डीबगिंग
में भेज देती हैं जो कभी समस्या ही नहीं था।
हस्ताक्षर एल्गोरिदम चुनना और जाँचना
व्यवहार में दो परिवार मायने रखते हैं। HS256 साझा सीक्रेट वाला HMAC है: जो जाँच सकता है, वह हस्ताक्षर भी कर सकता है — एक ही सेवा के अंदर यह ठीक है, संगठनों के बीच बुरी तरह बेमेल। RS256 और ES256 निजी कुंजी से हस्ताक्षर और सार्वजनिक कुंजी से जाँच करते हैं, जिससे कोई API वे टोकन सत्यापित कर सकता है जो वह ख़ुद कभी जारी नहीं कर पाता।
फिर alg: none है — अहस्ताक्षरित टोकन, जिसकी स्पेसिफ़िकेशन इजाज़त देती है और जिसे
आपको कभी स्वीकार नहीं करना चाहिए। यह मानक में है, इसलिए लाइब्रेरियों में रहा, इसलिए इसका दुरुपयोग
हुआ: जो जाँचकर्ता एल्गोरिदम टोकन के ही हेडर से उठाता है, उसे कहा जा सकता है “यह अहस्ताक्षरित
है, बस भरोसा कर लो”। एक मिलती-जुलती तरकीब RS256 जाँचकर्ता को ऐसा HS256 टोकन थमाती है जिस पर
सार्वजनिक कुंजी को ही HMAC सीक्रेट बनाकर हस्ताक्षर किया गया हो।
बचाव नीति की एक पंक्ति है: टोकन देखने से पहले तय करें कि आप कौन-से एल्गोरिदम स्वीकार
करते हैं, और बाकी सब ठुकरा दें। हेडर इस बारे में संकेत है कि कौन-सी कुंजी लेनी है, इस बारे
में निर्देश नहीं कि सिग्नेचर जाँचना है या नहीं। आपका डिकोडर alg: none को लाल रंग में
दिखाता है, तो वजह यही है।
पेलोड में कभी क्या नहीं जाना चाहिए
चूँकि पेलोड एन्कोडेड है, एन्क्रिप्टेड नहीं, हर क्लेम उसके लिए सार्वजनिक है जिसके पास टोकन हो — और टोकन ब्राउज़र स्टोरेज, लॉग, एरर-ट्रैकर, सपोर्ट टिकट और स्क्रीनशॉट तक पहुँच जाते हैं। पेलोड से बाहर रखें:
- जो कुछ आप URL में नहीं डालते: पासवर्ड, API कुंजियाँ, कार्ड नंबर, स्वास्थ्य संबंधी विवरण।
- वे आंतरिक पहचानकर्ता जिन्हें आप प्रकाशित नहीं करना चाहेंगे: डेटाबेस प्राइमरी की, आंतरिक होस्टनाम, लाइसेंस की शर्तें।
- बड़े ब्लॉब। हर रिक्वेस्ट टोकन ढोती है; एक किलोबाइट क्लेम हर कॉल पर एक किलोबाइट है, और बहुत बड़े टोकन प्रॉक्सी की हेडर आकार-सीमा से टकराने का भरोसेमंद तरीका हैं।
टोकन को URL से भी दूर रखें। क्वेरी स्ट्रिंग में रखा टोकन सर्वर लॉग, Referer हेडर
और ब्राउज़र इतिहास में उतर जाता है। उसे Authorization हेडर में भेजें, और यह पक्का करें
कि आपकी साइट समझदार Referrer-Policy सेट करती है: हमारी
सुरक्षा हेडर गाइड इसे कवर करती है, और
सुरक्षा हेडर चेकर बताएगा कि आप आज क्या
भेज रहे हैं।
समाप्ति, रिफ़्रेश और रद्द करने की समस्या
JWT वापस नहीं बुलाया जा सकता। एक बार जारी होने पर वह exp तक मान्य है, पीछे के खाते
का चाहे जो हो। किसी उपयोगकर्ता को निष्क्रिय कर दें, और उसका एक्सेस टोकन समय ख़त्म होने तक चलता
रहेगा। हर रिक्वेस्ट पर डेटाबेस न देखने की यही कीमत है।
मानक जवाब है दो टोकन। एक छोटी उम्र का एक्सेस टोकन — मिनटों का, दिनों का नहीं — क्लेम ढोता है और काम करता है। एक लंबी उम्र का रिफ़्रेश टोकन सर्वर पर रखा जाता है, हर इस्तेमाल पर डेटाबेस से जाँचा जाता है, और तुरंत रद्द किया जा सकता है। किसी को लॉग आउट करने का मतलब तब यह है कि रिफ़्रेश टोकन अमान्य कर दें और उस छोटी खिड़की का इंतज़ार करें जिसमें एक्सेस टोकन ख़ुद मर जाएगा।
समाप्ति की अवधि सोच-समझकर चुनें: यही वह अंतर है जो पहुँच रद्द करने और पहुँच असल में रुकने के बीच
रहता है। पंद्रह मिनट एक आम समझौता है। घंटों को जायज़ ठहराना कठिन है; दिनों का मतलब है कि आपके पास
रद्द करने की सुविधा ही नहीं। और अगर तुरंत रद्द करना ज़रूरी है, तो jti मानों की एक
निषेध-सूची रखें — और तब आप फिर हर रिक्वेस्ट पर एक भंडार देख रहे हैं, जहाँ सादा सेशन ही सरल डिज़ाइन
हो सकता है।
आप जो भी चुनें, डीबगिंग का चक्र वही रहता है: टोकन डिकोड करें, exp और
aud पढ़ें, घड़ी की पुष्टि करें, फिर सर्वर पर सिग्नेचर सत्यापित करें। चार चरण, इसी क्रम
में — और टोकन की लगभग हर समस्या पहले दो में ही निकल आती है।
संबंधित टूल और गाइड:
- JWT डिकोडर — कोई JSON Web Token डिकोड करें और उसका हेडर, पेलोड, क्लेम और समाप्ति पढ़ें।
- Base64 एनकोडर और डिकोडर — टेक्स्ट को Base64 में बदलें या वापस डिकोड करें, URL-safe आउटपुट और सही UTF-8 के साथ।
- Base64 समझें: एन्कोडिंग और डिकोडिंग असल में कैसे काम करती हैं — Base64 बाइट को टेक्स्ट में कैसे बदलता है
अक्सर पूछे जाने वाले प्रश्न
क्या मैं सीक्रेट कुंजी के बिना JWT डिकोड कर सकता हूँ?⌄
हाँ। हेडर और पेलोड Base64url में एन्कोडेड हैं, एन्क्रिप्टेड नहीं, इसलिए जिसके पास टोकन है वह उन्हें पढ़ सकता है। कुंजी केवल सिग्नेचर सत्यापित करने के लिए चाहिए। इसी वजह से पेलोड में संवेदनशील डेटा कभी नहीं रखना चाहिए।
किसी ऑनलाइन डिकोडर में JWT पेस्ट करना सुरक्षित है?⌄
तभी, जब डिकोडिंग आपके ब्राउज़र में हो। समाप्त होने तक टोकन एक जीवित क्रेडेंशियल है, इसलिए उसे सर्वर पर भेजने वाला टूल आपकी क्रेडेंशियल संभाल रहा है। हमारा डिकोडर पूरी तरह पेज में चलता है और टोकन कहीं नहीं भेजता, पर सबसे सुरक्षित आदत यही है कि जहाँ हो सके समाप्त या परीक्षण टोकन इस्तेमाल करें।
मेरा टोकन ठीक डिकोड होता है, फिर भी 401 क्यों आता है?⌄
डिकोड होना केवल यह साबित करता है कि स्ट्रिंग ठीक बनी है। आम कारण हैं बीत चुका exp, जारीकर्ता और जाँचकर्ता के बीच घड़ी का अंतर, किसी दूसरे API का नाम लेने वाला aud, या कुंजी बदलने के बाद ऐसा kid जिसे जाँचकर्ता हल नहीं कर पाता।
JWT और सेशन कुकी में क्या फ़र्क है?⌄
सेशन कुकी एक सूचक है: सर्वर स्थिति रखता है और हर रिक्वेस्ट पर उसे देखता है, इसलिए उसे तुरंत रद्द किया जा सकता है। JWT स्थिति ख़ुद ढोता है, इसलिए कुछ देखने की ज़रूरत नहीं — और समाप्ति से पहले कुछ वापस भी नहीं लिया जा सकता। JWT सेवाओं के बीच बेहतर स्केल करता है; सेशन आपको नियंत्रण देते हैं।
मुझे HS256 या RS256, क्या इस्तेमाल करना चाहिए?⌄
HS256 एक ही सीक्रेट साझा करता है, इसलिए जो जाँच सकता है वह हस्ताक्षर भी कर सकता है। एक ही सेवा के अंदर यह स्वीकार्य है। RS256 और ES256 निजी कुंजी से हस्ताक्षर और सार्वजनिक से जाँच करते हैं, और एक से अधिक पक्ष होते ही आपको यही चाहिए। किसी भी हाल में, स्वीकार्य एल्गोरिदम अपने जाँचकर्ता में तय कर दें और टोकन के alg मान पर कभी भरोसा न करें।
वेब डेवलपमेंट और सुरक्षा में 15+ वर्षों का व्यावहारिक अनुभव। व्यावहारिक वेब सुरक्षा पर ध्यान: वेबसाइटों को सुरक्षित, भरोसेमंद और समस्या-निवारण में आसान रखना। BuildToolForge इन जाँचों को मुफ़्त, निजी और समझने योग्य बनाने के लिए है। जहाँ संभव हो, टूल आपके ब्राउज़र में चलते हैं, और आप किसी टूल में जो कुछ भी दर्ज करते हैं वह संग्रहीत नहीं होता।
हम अपनी गाइड कैसे लिखते हैं: हर गाइड व्यावहारिक अनुभव से लिखी जाती है, आधिकारिक मानकों से जाँची जाती है और असली डोमेन पर हमारे अपने टूल से परखी जाती है। मानक या टूल बदलने पर हम गाइड अपडेट करते हैं। कोई गलती मिली? हमें contact@buildtoolforge.com पर ईमेल करें।