वन-टाइम सीक्रेट लिंक ऐसा URL है जो अपनी सामग्री ठीक एक बार दिखाता है और फिर हमेशा के लिए बेकार हो जाता है। भेजने वाले का ब्राउज़र सीक्रेट को AES-GCM-256 से एन्क्रिप्ट करता है, सर्वर सिर्फ़ एन्क्रिप्ट किया हुआ डेटा (सिफ़रटेक्स्ट) स्टोर करता है, और कुंजी URL में #k= के बाद वाले हिस्से में रहती है, जिसे ब्राउज़र कभी सर्वर को नहीं भेजते। प्राप्तकर्ता जब पहली बार सीक्रेट खोलता है, तो उसी एक ऑपरेशन में डेटा डिक्रिप्ट होता है और स्टोर की गई कॉपी मिट जाती है।
बिना अकाउंट के ऐसा लिंक मुफ़्त सीक्रेट लिंक जनरेटर से बनाया जा सकता है।
तरीका सीधा है, लेकिन असली बातें खास हालात (edge cases) में सामने आती हैं, और वहीं लापरवाही से बने टूल चुपचाप डेटा लीक कर देते हैं।
बुनियादी प्रक्रिया
- भेजने वाला सीक्रेट पेस्ट करता है। ब्राउज़र एक रैंडम कुंजी बनाता है, Web Crypto API के ज़रिए डेटा को AES-GCM-256 से एन्क्रिप्ट करता है और सिर्फ़ एन्क्रिप्ट किया हुआ डेटा भेजता है। सर्वर उसे एक रैंडम, मुश्किल से अंदाज़ा लगने वाले स्लग के तहत स्टोर करता है और URL लौटाता है; ब्राउज़र कुंजी को
#k=वाले हिस्से के रूप में URL के आखिर में जोड़ देता है, और यह हिस्सा भेजने वाले के डिवाइस से बाहर कभी नहीं जाता। - भेजने वाला URL किसी भी ज़रिए से शेयर करता है: ईमेल, चैट या SMS।
- प्राप्तकर्ता URL खोलता है। सामान्य
GETरिक्वेस्ट सिर्फ़ स्टेटस लौटाती है। “दिखाएं” पर क्लिक करते ही सर्वर एक ही ऑपरेशन में, जिसे बीच से तोड़ा नहीं जा सकता, सीक्रेट पर इस्तेमाल हो चुके का निशान लगाता है और एन्क्रिप्ट किया हुआ डेटा लौटाता है; प्राप्तकर्ता का ब्राउज़र URL वाले हिस्से की कुंजी से उसे डिक्रिप्ट करता है। - इसके बाद कोई भी लिंक खोले, तो उसे सामग्री की जगह एक पेज दिखता है कि सीक्रेट मिट चुका है।
सबसे अहम कदम तीसरा है। अगर पढ़ना और स्टेटस बदलना दो अलग क्वेरी में हों, यानी जांचने के लिए SELECT और फिर निशान लगाने के लिए UPDATE, तो एक साथ खोलने वाले दो लोग किसी के लिखने से पहले ही जांच पार कर सकते हैं। दोनों को डेटा मिल जाता है। यह TOCTOU (time-of-check to time-of-use) नाम का जाना-पहचाना बग है, और यह आपकी “ठीक एक बार” वाली गारंटी को “आम तौर पर एक बार” में बदल देता है।
सीक्रेट का मिटना और पढ़ा जाना एक ही ऑपरेशन में क्यों होना चाहिए?
क्योंकि वरना एक साथ खोलने वाले दो लोग, सीक्रेट पर इस्तेमाल का निशान लगने से पहले ही, दोनों जांच पार कर सकते हैं। इसका इलाज है जांच, निशान और पढ़ने को एक ही डेटाबेस ऑपरेशन में समेट देना। Postgres में UPDATE ... RETURNING से यह ऐसा दिखता है:
UPDATE secrets
SET burned_at = now()
WHERE slug = $1
AND burned_at IS NULL
AND expires_at > now()
RETURNING payload;
UPDATE जो row-level लॉक लेता है, उसकी वजह से एक साथ आई रिक्वेस्ट एक-एक करके चलती हैं: पहली रिक्वेस्ट WHERE शर्त से मेल खाती है, burned_at भरती है और RETURNING से डेटा वापस पाती है। उसके बाद आने वाली हर रिक्वेस्ट को burned_at पहले से भरा मिलता है, कोई row मेल नहीं खाती, और उसे “मिट चुका है” वाला स्टेटस मिलता है। “जांच” और “इस्तेमाल” के बीच कोई खाली समय नहीं बचता, क्योंकि दोनों एक ही स्टेटमेंट हैं।
LinkPilot ठीक इसी वजह से व्यू को एक डेटाबेस फ़ंक्शन के ज़रिए एक ही अटूट कदम में दर्ज करता है: एक साथ खोलने की कई कोशिशें सीक्रेट को दो बार लीक नहीं कर सकतीं। अगर आप कोई टूल परख रहे हैं, तो वेंडर से सीधे यह पूछना जायज़ है: क्या सीक्रेट का पढ़ा जाना और मिटना एक ही ऑपरेशन में, एक साथ होता है?
मिट चुके लिंक HTTP 404 नहीं, 410 क्यों लौटाते हैं?
क्योंकि 410 Gone का मतलब है कि रिसोर्स मौजूद था और अब हमेशा के लिए उपलब्ध नहीं है, और मिट चुका सीक्रेट ठीक यही है। इस्तेमाल हो जाने के बाद लिंक को साफ़ तौर पर और हमेशा के लिए फ़ेल होना चाहिए। सीक्रेट मिटने पर LinkPilot दो काम करता है:
- स्टोर किया गया डेटा उसी ऑपरेशन में
[REDACTED]से ओवरराइट कर दिया जाता है जिसमें स्टेटस बदलता है। इसलिए बाद में डेटाबेस में सेंध लग भी जाए, तो इस्तेमाल हो चुका सीक्रेट वापस नहीं निकाला जा सकता। - इसके बाद दिखाने की हर कोशिश पर HTTP 410 Gone लौटता है, साथ में एक साफ़ पेज कि सीक्रेट “पहले ही देखा जा चुका है और मिटा दिया गया है”।
410 ही सही स्टेटस कोड है: इसका मतलब है कि रिसोर्स मौजूद था और जान-बूझकर, हमेशा के लिए हटाया गया है। इसके उलट 404 कहता है “इसके बारे में कभी सुना ही नहीं”। यह फ़र्क लोगों के लिए भी मायने रखता है: जिस प्राप्तकर्ता को “यह सीक्रेट पहले ही देखा जा चुका है” दिखता है, उसे पता चल जाता है कि भेजने वाले को बताना है। भेजने वाला ऑडिट ट्रेल देख सकता है, और अगर देखने वाला प्राप्तकर्ता नहीं था, तो पासवर्ड बदल सकता है।
मुश्किल से अंदाज़ा लगने वाले स्लग, और वे काफ़ी क्यों नहीं हैं
सार्वजनिक इंटरनेट और बिना पढ़े एन्क्रिप्ट किए हुए डेटा के बीच सिर्फ़ स्लग खड़ा है (कुंजी सर्वर पर नहीं, लिंक में # के बाद वाले हिस्से में है), इसलिए उसमें असली रैंडमनेस (entropy) होनी चाहिए। मान लें कि स्लग क्रिप्टोग्राफ़िक रूप से सुरक्षित रैंडम स्रोत से base62 के 22 अक्षरों में बना है। तब उसमें लगभग 128 बिट होते हैं, और किसी भी ऐसी रिक्वेस्ट दर पर, जिसे सर्वर झेल सके, सारे स्लग एक-एक करके आज़माना कोई असली हमला नहीं है।
लेकिन स्लग की रैंडमनेस सिर्फ़ अंदाज़ा लगाने से बचाती है। अगर URL किसी की नज़र में आ जाए, तो यह कुछ नहीं करती: ब्राउज़र हिस्ट्री, प्रॉक्सी लॉग, फ़ॉरवर्ड किए गए ईमेल, स्क्रीन शेयर, क्लिपबोर्ड मैनेजर। इसीलिए गंभीर टूल इसके ऊपर ये परतें जोड़ते हैं:
- एक्सपायरी, ताकि नज़र में आया URL तय समय पर बेकार हो जाए।
- पासफ़्रेज़, ताकि सिर्फ़ URL मिल जाना काफ़ी न हो।
- पढ़ते ही मिट जाना, ताकि सही व्यक्ति के पढ़ लेने के बाद नज़र में आया URL किसी काम का न रहे।
लिंक प्रीव्यू और बॉट
चैट ऐप (Slack, Teams, iMessage) और ईमेल के सिक्योरिटी स्कैनर मैसेज पहुंचते ही GET रिक्वेस्ट से URL खोलकर उसका प्रीव्यू बनाते हैं। अगर आपका “दिखाएं” वाला endpoint GET पर ही सीक्रेट मिटा देता है, तो लिंक प्रीव्यू बॉट इंसान के देखने से पहले ही सीक्रेट मिटा देता है। पढ़ते ही मिटने वाले कच्चे टूल असल में सबसे ज़्यादा इसी वजह से फ़ेल होते हैं।
LinkPilot इससे ऐसे बचाता है:
- मेटाडेटा और दिखाने की कार्रवाई अलग-अलग। “दिखाएं” वाले endpoint पर
GETसिर्फ़ स्टेटस लौटाता है और यह बताता है कि पासफ़्रेज़ चाहिए या नहीं। व्यू सिर्फ़ साफ़ तौर पर भेजे गएPOSTपर दर्ज होता है, जो तब होता है जब कोई इंसान “दिखाएं” बटन पर क्लिक करता है। - पासफ़्रेज़ की शर्त (Pro)। अगर पासफ़्रेज़ सेट है, तो सही हैश के बिना किसी को भी डेटा नहीं मिलता, चाहे बॉट हो या इंसान।
Cache-Control: no-storeहर “दिखाएं” वाले जवाब पर, ताकि बीच के सर्वर सीक्रेट की सामग्री कभी कैश न करें।
LinkPilot के सिक्योरिटी आर्किटेक्चर पेज पर यह पूरी प्रक्रिया शुरू से आखिर तक लिखी है।
लिंक का जीवनचक्र, हर स्थिति के साथ
| स्थिति | किस वजह से | खोलने वाले को क्या दिखता है | क्या डेटा वापस मिल सकता है? |
|---|---|---|---|
| “active” (सक्रिय) | लिंक बनना | “दिखाएं” वाला पेज (अगर सेट है तो पासफ़्रेज़ मांगा जाता है) | हां, एक बार दिखाया जा सकता है |
| “burned” (मिट चुका) | व्यू की सीमा पूरी होना | “पहले ही देखा जा चुका है और मिटा दिया गया है”, HTTP 410 | नहीं, डेटा ओवरराइट हो चुका है |
| “expired” (एक्सपायर) | समय-सीमा खत्म होना | एक्सपायर होने का स्टेटस पेज | नहीं, अगली बार खुलने पर हटा दिया जाता है |
| “revoked” (वापस लिया गया) | भेजने वाला डैशबोर्ड से वापस ले लेता है | दिखाने से इनकार, HTTP 410 | नहीं, तुरंत और हमेशा के लिए |
हर बदलाव ऑडिट टाइमलाइन में दर्ज होता है, जो LinkPilot में Pro की सुविधा है। इवेंट के प्रकार हैं created, viewed, failed_passphrase, burned, expired और revoked, हर एक के साथ समय और हैश किया हुआ IP (SHA-256, जिसका salt रोज़ बदलता है; असली IP कभी स्टोर नहीं होता)। यही रिकॉर्ड “मुझे लगता है उन्हें मिल गया” को “10:05 पर, उसी देश से जहां से उम्मीद थी, एक बार देखा गया, फिर मिट गया” में बदलता है।
आम तौर पर जोड़ी जाने वाली सुविधाएं
- पासफ़्रेज़ की शर्त: दिखाने से पहले एक पासफ़्रेज़ मांगा जाता है जो किसी दूसरे ज़रिए से भेजा गया हो, ताकि सिर्फ़ URL लीक होने से सीक्रेट लीक न हो। LinkPilot में यह Pro की सुविधा है ($29/माह), साथ में अपनी पसंद की एक्सपायरी और ऑडिट टाइमलाइन भी; बिना अकाउंट वाला जनरेटर पासफ़्रेज़ सेट नहीं कर सकता। कोशिशों की संख्या सीमित रखें; LinkPilot हर सीक्रेट के लिए हर IP से प्रति मिनट 5 कोशिशें करने देता है और हर विफल कोशिश दर्ज करता है।
- समय के हिसाब से एक्सपायरी: तय समय के बाद सीक्रेट खत्म हो जाता है, भले ही किसी ने पढ़ा न हो। LinkPilot का साइन-इन वाला फ़ॉर्म हर प्लान पर 1 घंटा, 24 घंटे या 7 दिन देता है, और Pro पर अपनी पसंद की तारीख और समय; बिना अकाउंट वाला जनरेटर डिफ़ॉल्ट रूप से 7 दिन रखता है। इससे वह मामला संभलता है जब लिंक इनबॉक्स में भूला पड़ा रहे, जिसे पढ़ते ही मिटना नहीं संभाल सकता।
- हाथ से वापस लेना: कुछ भी गड़बड़ लगे, तो भेजने वाला डैशबोर्ड से बिना पढ़े सीक्रेट को उसी पल वापस ले सकता है। “गलत Alex को भेज दिया” जैसी गलती के लिए यह सस्ता बचाव है।
खास हालात, जिनसे अच्छे टूल की पहचान होती है
गलत व्यक्ति पहले खोल लेता है
पढ़ते ही मिटना गलत व्यक्ति को देखने से नहीं रोकता; यह गलत जगह पहुंचे लिंक को दिखने लायक बना देता है। सही प्राप्तकर्ता को “मिट चुका है” वाला पेज मिलता है, ऑडिट ट्रेल में एक ऐसा व्यू दिखता है जिसकी आपको उम्मीद नहीं थी, और आप वह लॉगिन की जानकारी बदल देते हैं। तकलीफ़ होती है, लेकिन नुकसान सीमित रहता है: ठीक एक बार जानकारी उजागर हुई, और जल्दी पता चल गया। इसकी तुलना चैट में सीधे पेस्ट किए गए सीक्रेट से करें, जहां आपको कभी पता ही नहीं चलता।
स्क्रीनशॉट की समस्या
एक बार का मतलब है एक बार दिखना, एक कॉपी नहीं। प्राप्तकर्ता सीक्रेट का स्क्रीनशॉट ले सकता है, उसे कॉपी कर सकता है या दोबारा टाइप कर सकता है; लिंक इसमें से कुछ नहीं रोकता, और जो वेंडर इसका उलटा इशारा करते हैं, वे झूठ बेच रहे हैं। सच यह है: पढ़ते ही मिटना भेजने के रास्ते को नियंत्रित करता है, प्राप्तकर्ता को नहीं। प्राप्तकर्ता पर भरोसा पहले से होना चाहिए; यह टूल उसे पैदा नहीं करता।
ब्राउज़र का प्रीफ़ेच और पहले से लोड करना
कुछ ब्राउज़र और ऐप उन URL को पहले से लोड कर लेते हैं जिन पर क्लिक होने की संभावना होती है। अच्छा टूल सिर्फ़ साफ़ तौर पर भेजे गए POST पर सीक्रेट मिटाता है, इसलिए प्रीफ़ेच भी लिंक प्रीव्यू बॉट जैसा ही बर्ताव करता है: वह पेज का ढांचा ला सकता है, डेटा कभी नहीं।
वन-टाइम लिंक कब इस्तेमाल न करें
- जानकारी फिर से चाहिए होगी। पासवर्ड मैनेजर का पूरा मकसद ही यह है कि जानकारी वापस मिल सके, और एक बार खुलने वाले लिंक का मकसद ठीक इसका उलटा है। पासवर्ड मैनेजर के वॉल्ट में आइटम रखें।
- कई प्राप्तकर्ताओं को चाहिए। एक लिंक, एक बार पढ़ना। कई लोगों को भेजना हो, तो या तो हर व्यक्ति के लिए अलग लिंक बनाएं (यह ठीक है और इसका ऑडिट हो सकता है), या मान लें कि आपको साझा एक्सेस कंट्रोल चाहिए।
- इस्तेमाल करने वाली एक मशीन है। सर्विस और CI को सीक्रेट मैनेजर से पढ़ना चाहिए, लिंक पर क्लिक करके नहीं।
अगर आप यह प्रक्रिया काम करते हुए देखना चाहते हैं, तो मुफ़्त सीक्रेट लिंक जनरेटर से एक लिंक बनाएं: अकाउंट बनाने की ज़रूरत नहीं, आपके ब्राउज़र में ही एन्क्रिप्ट होता है, और पहली बार दिखने के बाद मिट जाता है।
इससे जुड़े लेख
- पासवर्ड सुरक्षित तरीके से शेयर करना: 5 तरीकों की तुलना
- वन-टाइम सीक्रेट, वन-टाइम लिंक और सीक्रेट लिंक: छोटे पेज, जिनमें सबसे ऊपर टूल है
- क्या OneTimeSecret सुरक्षित है?
- OneTimeSecret के सबसे अच्छे विकल्प
- OneTimeSecret बनाम Privnote बनाम Password Pusher
- एक्सपायर होने वाले लिंक: सीक्रेट मिटा दिया जाता है, सिर्फ़ रीडायरेक्ट नहीं होता
- मुफ़्त सीक्रेट लिंक जनरेटर
संदर्भ
- W3C, Web Cryptography API।
- IETF, RFC 3986, section 3.5 Fragment।
- IETF, RFC 9110: HTTP Semantics, section 15.5.11 410 Gone।
- PostgreSQL Documentation, UPDATE।
