- UUIDv47 डेटाबेस में sortable UUIDv7 स्टोर करते हुए, बाहरी API में UUIDv4 जैसा दिखने वाला मान प्रदान करता है
- केवल timestamp field को XOR masking करके UUIDv7 की time information को सुरक्षित रखता है, और बाकी random fields को ज्यों का त्यों बनाए रखता है
- SipHash-2-4 का उपयोग करने वाली 128-bit key से masking करके, key exposure के जोखिम के बिना सुरक्षित रूप से जानकारी की सुरक्षा संभव है
- encode/decode deterministic और reversible हैं, और randomness बनी रहती है, इसलिए collision risk कम है
- benchmark results बहुत तेज़ performance और आसान integration method प्रदान करते हैं, और PostgreSQL जैसे डेटाबेस के साथ आसानी से जोड़ा जा सकता है
प्रोजेक्ट का अवलोकन और महत्व
- UUIDv47 एक open-source C library है, जो डेटाबेस के अंदर sorting और indexing के लिए उपयुक्त UUIDv7 स्टोर करती है, जबकि बाहरी API और सिस्टम में UUIDv4 जैसा दिखने वाला मान expose करती है, ताकि privacy protection और high-performance processing दोनों एक साथ हासिल किए जा सकें
- अन्य UUID conversion algorithms की तुलना में इसमें reversible mapping, RFC compatibility, key recovery impossible जैसी security, zero-deps, और केवल simple header file include करने वाली संरचना जैसे अलग तरह के मजबूत फायदे हैं
मुख्य विशेषताएँ
- Header-only C (C89), बिना external dependency के सरल integration संभव
- UUIDv7 के केवल timestamp field को XOR masking करके time information exposure को रोकता है, और बाकी random fields को बदलता नहीं
- keyed SipHash-2-4 से masking करके 128-bit key के साथ सुरक्षित information protection संभव
- encode/decode process deterministic और पूरी तरह reversible है (मूल रूप को बिल्कुल restore किया जा सकता है)
- डेटाबेस स्टोरेज के लिए (v7) और बाहरी exposure के लिए (v4) UUID के बीच तेज़ mapping support
- test code और benchmark tools सहित कई उदाहरण उपलब्ध
उपयोग का उद्देश्य और लाभ
- DB में index locality और paging efficiency को अधिकतम करने के लिए sortable UUIDv7 का उपयोग किया जा सकता है
- बाहर केवल UUIDv4 जैसा दिखने वाला pattern expose करके timestamp leak और tracking को रोकता है
- SipHash का उपयोग करके key recovery असंभव, secret key safety सुनिश्चित
- RFC-compatible version/variant bit handling
- बहुत तेज़ होने के कारण real-time processing और large-scale generation environments में भी प्रभावी
मुख्य संरचना और आंतरिक कार्यप्रणाली
UUIDv7 Layout
- ts_ms_be: 48-bit big-endian timestamp
- ver: high nibble 6th byte (0x7=DB, 0x4=external)
- rand_a: 12-bit random value
- var: RFC variant (0b10)
- rand_b: 62-bit random value
masking और mapping logic (Façade mapping)
- encoding: ts48 XOR mask48(R), version=4 set
- decoding: encTS XOR mask48(R), version=7 set
- random fields में कोई बदलाव नहीं
- SipHash input के रूप में 10-byte random field का उपयोग
- XOR masking में key पता हो to तुरंत reverse conversion संभव
सुरक्षा मॉडल
- लक्ष्य: selective input देने पर भी key exposed न हो
- implementation: SipHash-2-4 नामक keyed pseudo-random function (PRF) का उपयोग
- 128-bit key का उपयोग, HKDF आदि के माध्यम से key derivation की सिफारिश
- key rotation के समय UUID के अंदर स्टोर न करके, अलग छोटे key ID को बनाए रखने की सिफारिश
public API (C)
- uuidv47_encode_v4facade : v7→v4 conversion
- uuidv47_decode_v4facade : v4→v7 restore
- version setting, parsing, formatting से जुड़ी अन्य functions भी उपलब्ध
प्रदर्शन और benchmark
- SipHash masking (10B) operation में 14ns/op से कम, encode+decode full round trip 33ns/op स्तर (Apple M1 के आधार पर)
- बड़े पैमाने पर UUID generation·mapping में भी तेज़ processing की गारंटी
- -O3 -march=native options पर सर्वोत्तम performance
integration और विस्तार
- API boundary पर encode/decode processing की सिफारिश
- PostgreSQL integration के लिए C extension लिखें
- sharding करते समय v4 façade को xxh3, SipHash आदि से hash किया जा सकता है
अन्य
- अन्य भाषा port: Go(n2p5/uuid47) आदि उपलब्ध
- अनुशंसित hash: xxHash non-PRF होने के कारण information leak की चिंता, SipHash उपयोग की सिफारिश
लाइसेंस
- MIT लाइसेंस (Stateless Limited, 2025)
1 टिप्पणियां
Hacker News की राय
नमस्ते, मैं uuidv47 का लेखक हूँ। मूल विचार यह है कि अंदरूनी तौर पर UUIDv7 का उपयोग करके database indexing और sortability बनाए रखें, लेकिन बाहर UUIDv4 जैसा दिखने वाला मान दें ताकि clients के सामने timing pattern उजागर न हों।
यह 48-bit timestamp को UUID के random field से निकली SipHash-2-4 stream के साथ XOR mask करके काम करता है।
random bits वैसे ही बने रहते हैं, version अंदर 7 और बाहर 4 में बदल जाता है, और RFC variant value भी बनी रहती है।
mapping injective है:
(ts, rand) → (encTS, rand)संरचना।decode
encTS ⊕ maskतरीके से होता है, इसलिए पूरी round-trip conversion संभव है।security के लिहाज़ से SipHash एक PRF है, इसलिए बाहर से packaged value देखने पर भी key उजागर नहीं होती।
अगर key गलत हो तो timestamp भी पूरी तरह अलग निकलता है।
बाहरी key-ID management के साथ key rotation भी support की जा सकती है।
performance के हिसाब से 10 bytes पर एक SipHash, कुछ 48-bit load/store भर हैं, इसलिए overhead nanosecond स्तर का है; यह C11 header-only है, कोई external dependency नहीं है, और allocation भी नहीं चाहिए।
testing में SipHash reference vectors, round-trip encode/decode, और version/variant invariance की जाँच की गई है।
feedback जानना चाहूँगा।
यह idea मुझे पसंद आया।
UUID अक्सर client side पर generate किए जाते हैं, लेकिन इस तरीके में यह संभव नहीं लगता।
अगर client द्वारा बनाए गए UUID लेकर masked version लौटाया जाए, तो क्या कोई व्यक्ति ऐसे दो UUID नहीं दे सकता जिनमें
tsअलग हो लेकिनrandएक जैसा हो, और इससे कमजोरी पैदा हो?यानी क्या यह तरीका आखिरकार केवल उसी स्थिति के लिए उपयुक्त है जहाँ UUIDv7 सीधे server side पर generate किया जाए?
मेरी दो टिप्पणियाँ हैं।
पता नहीं यह अतिरिक्त झंझट उसकी कीमत के लायक है या नहीं।
मेरी सबसे बड़ी चिंता random bits की entropy quality है।
UUIDv7 collision avoidance पर ज़्यादा केंद्रित है, इसलिए इसकी प्राथमिकता predictability से अधिक collisions से बचना है।
इसी वजह से RFC में non-randomness के लिए must की बजाय should लिखा गया है, और ऐसी implementations भी हैं जो weak PRNG या counter का उपयोग करती हैं, यहाँ तक कि random bits की जगह अतिरिक्त clock data डाल देती हैं (संदर्भ: RFC9562 s6.2 & s6.9)।
इसलिए अगर v7 का
rand_a,rand_bभरोसे की सीमा के बाहर से आए डेटा हों, तो उन्हें सीधे PRF seed के रूप में इस्तेमाल करना अपेक्षा से अधिक जोखिमभरा हो सकता है।PostgreSQL 18 का नया
uuidv7()भी high-precision timestamp सेrand_aको पूरा भर देता है, और RFC के हिसाब से यह भी ठीक है।bulk import में बने UUID देखें तो यह v7-to-v4 तरीका भी अंततः grouping की अनुमति दे सकता है, इसलिए जानकारी लीक हो सकती है।
engine parts telemetry जैसी चीज़ों में शायद समस्या न हो, लेकिन अगर डेटा सीधे लोगों से जुड़ी पहचान का हो, तो सावधानी ज़रूरी है।
नतीजतन, जब तक आप खुद भरोसेमंद entropy सुनिश्चित न करें, यह scheme भी timing, serial, या correlation संबंधी जानकारी लीक कर सकती है; इसलिए v7 implementation के source को सीधे जाँचना चाहिए।
मुझे यह अच्छा idea नहीं लगता।
PostgreSQL 18 में optional parameter
shiftदिए गए अंतराल जितना timestamp को आगे-पीछे कर देता है।https://www.postgresql.org/docs/18/functions-uuid.html
कुछ साल पहले मैंने अपनी scheme बनाई थी जिसमें DB में sequential numeric ID रखता था और बाहर 4~20 अक्षरों की छोटी random string दिखाता था।
इसमें मैंने Speck cipher family की एक custom instance का उपयोग किया था, और मुझे यह मज़बूत और काफ़ी अच्छा लगा।
मैंने इसे पूरा तो कर लिया था, लेकिन जिस project में इस्तेमाल करना था उसे टाल दिया, इसलिए प्रकाशित नहीं किया।
इस साल या अगले साल मैं उस सामग्री को औपचारिक रूप से जारी करने की योजना बना रहा हूँ।
implementation, फायदे और नुकसान पर अच्छे notes भी हैं, रुचि हो तो देख सकते हैं।
https://temp.chrismorgan.info/2025-09-17-tesid/
मैंने भी पहले Speck से
bigserialPKID को obfuscate करने की कोशिश की थी, लेकिन cross-platform implementations कम थीं, और खासकरpgcryptoमें support कमजोर था।इसलिए मैंने
base58(AES_K1(id{8} || HMAC_K2(id{8})[0..7]))चुना।output आमतौर पर लगभग 22 characters का होता है, यानी थोड़ा लंबा है, लेकिन लगभग हर environment में implement किया जा सकता है और performance भी काफ़ी संतोषजनक है।
अच्छा idea है।
मिलते-जुलते concept के रूप में sqids (पहले नाम: hashids) भी देखने लायक है।
https://sqids.org/
पहले मेरा भी कुछ ऐसा ही अनुभव रहा है: मैंने public UUID और API में expose न होने वाला bigint PK, ऐसे दो columns रखकर काम किया था (यह UUIDv7 आने से बहुत पहले की बात है)।
UUID की convenience थोड़ी कम थी, लेकिन अगर PK को ठीक से अलग रखो तो अलग-अलग DB dumps को आसानी से merge किया जा सकता था, यह बड़ा फ़ायदा था।
hash-based lookup करने पर भी शायद अंत में दो columns रखने ही पड़ेंगे; हो सकता है मैं hash की कार्यप्रणाली को गलत समझ रहा हूँ।
request में आए uuidv4 value को DB के uuidv7 में बदला जा सकता है।
idea दिलचस्प है, लेकिन इच्छा यह है कि database खुद इस तरह की प्रक्रिया को support करे।
यानी UUIDv7 को “UUIDv4” के साथ परस्पर रूप से convert किया जा सके, और queries में भी दोनों formats को स्पष्ट रूप से अलग करके इस्तेमाल किया जा सके।
यह सच में शानदार project है।
मैंने dchest की siphash library का उपयोग करके इसका Go implementation भी बनाया है।
https://github.com/n2p5/uuid47
संदर्भ: https://github.com/dchest/siphash
project दिलचस्प है, लेकिन क्या आप कोई वास्तविक उदाहरण दिखा सकते हैं कि uuid v7 में time component उजागर होने का जोखिम कैसे सामने आता है?
यदि user behavior pattern या sequence उजागर हो जाए, तो यह समस्याजनक स्थितियाँ पैदा कर सकता है।
individual messages या real-time transactions में शायद फ़र्क न पड़े, लेकिन user account creation या long-term data जैसी चीज़ों में कोई इसका इस्तेमाल पहचान जोड़ने के लिए कर सकता है।
मैंने पहले एक CTF में UUID के हिस्से को AES key के रूप में brute force किया था।
क्योंकि key आंशिक रूप से time source से derive की गई थी, key generation के समय का system time पता चलते ही हमला संभव हो गया।
एक और सरल उदाहरण: अगर कोई file sharing service सिर्फ
website.com/GUIDजैसी संरचना प्रकाशित करे और file upload time अलग से न दिखाए,तब भी UUIDv7 खुद file upload time का अनुमान लगाने दे सकता है।
यह ज़रूरी नहीं कि बहुत बड़ा security threat हो, लेकिन यह अनचाहा information disclosure है।
उदाहरण के लिए, मान लीजिए कोई system medical data store करता है।
analysis के लिए MRI scan के तुरंत बाद result upload किया जाता है, और कहा जाता है कि personal information हटा दी जाएगी।
फिर भी uuidv7 timestamp के आधार पर बाहरी correlation analysis संभव है, जिससे “इस तारीख़ को MRI कराने वाला सिर्फ एक व्यक्ति था, इसलिए यह उसी का MRI होगा” जैसी पहचान निकाली जा सकती है।
UUIDv7 की सबसे असुविधाजनक बात यह लगती है कि list में इंसान के लिए उसे आँख से compare (
diff) करना बहुत मुश्किल है।अगर
psqlमें ऐसा visualization layer हो जिसमें random bits आगे आ जाएँ और actual sort timestamp के आधार पर बना रहे, तो UX में बहुत बड़ा सुधार होगा।मैंने तो बस UUID के आख़िरी हिस्से को देखने की आदत डाल ली है।
आप खुद एक function बनाकर query में इस्तेमाल कर सकते हैं।
उदाहरण के लिए, hex representation के बाद string reverse करना, या reversed base64 में दिखाना—इससे यह छोटा भी लगेगा और अलग-अलग पहचानना भी आसान होगा।
यह तरीका काफ़ी ठीक लग रहा है।
लेकिन timestamp exposure पर बहुत ज़्यादा घबराहट, और sequential ID exposure को सीधे attack surface या business info leak से जोड़ना, मुझे असली security issue से ज़्यादा बेवजह की चिंता लगता है।
बस समय-समय पर
intvalue में कोई बड़ा random offset जोड़ दीजिए; monotonic बढ़त भी बनी रहेगी और बाहरी observer के लिए pattern पकड़ना मुश्किल होगा।आख़िरकार मुझे लगता है कि महत्वपूर्ण जानकारी लीक होने की चिंता के नाम पर थोड़ा ज़्यादा बढ़ा-चढ़ाकर कहा जाता है।
system से निकलने वाली जानकारी अपने आप में शायद बहुत मायने न रखे, लेकिन अगर इसे बड़े पैमाने या time series में देखा जाए, तो इससे अतिरिक्त डेटा infer किया जा सकता है।
उदाहरण के तौर पर, David Kriesel की SpiegelMining talk की तरह, अगर आप सिर्फ़ newspaper article की तारीख़ और author scrape करें, तब भी कौन कब छुट्टी पर गया इसका pattern निकाला जा सकता है।
कई authors के डेटा की तुलना करते-करते दफ़्तर के romantic relationships तक सामने आ सकते हैं।
यह क्यों नहीं कि हर session के लिए अलग cryptographic key हो और बाहर सिर्फ encrypted id ही expose किया जाए?
तब DB में साधारण sequential id ही पर्याप्त नहीं होगी क्या?
अगर key को नियमित रूप से बदलें, तो key management बहुत जटिल हो जाएगा, और हर बार सही key कैसे खोजी जाए यह भी समस्या होगी।
version 4 की बजाय version 8 क्यों नहीं चुना गया?
v4 का मतलब random bits होता है, लेकिन वास्तव में यह इतना random नहीं है।
v8 में bits के अर्थ पर ऐसा प्रतिबंध नहीं है।
इस तरीके का उद्देश्य ही बाहर से random दिखना है, इसलिए संभव है कि v8 उल्टा ज़्यादा ध्यान खींचता।