- UUIDv47, veritabanında sıralanabilir UUIDv7 saklarken dış API'lere UUIDv4 gibi görünen değerler sunar
- Yalnızca timestamp alanı XOR ile maskelenir; böylece UUIDv7'nin zaman bilgisi korunurken diğer rastgele alanlar aynen kalır
- SipHash-2-4 kullanan 128 bit anahtar ile maskeleme yaparak, anahtarın açığa çıkma riski olmadan bilgiyi güvenli biçimde korumak mümkündür
- encode/decode işlemleri deterministik ve tersinirdir; rastgelelik korunduğu için çakışma riski düşüktür
- Benchmark sonuçları, çok hızlı performans ve basit entegrasyon sunar; PostgreSQL gibi veritabanlarıyla kolayca entegre edilebilir
Projeye genel bakış ve önemi
- UUIDv47, veritabanı içinde sıralama ve indeksleme için avantajlı UUIDv7 saklarken, dış API'ler ve sistemlere UUIDv4 gibi görünen değerler göstererek gizlilik koruması ile yüksek performanslı işlemeyi aynı anda sağlayan açık kaynaklı bir C kütüphanesidir
- Diğer UUID dönüşüm algoritmalarına kıyasla tersinir eşleme, RFC uyumluluğu, anahtarın geri elde edilememesine dayalı güvenlik, zero-deps yapısı ve yalnızca basit bir header dosyası eklenerek kullanılabilmesi gibi alanlarda ayırt edici güçlü yönlere sahiptir
Başlıca özellikler
- Header-only C (C89), harici bağımlılık olmadan kolay entegrasyon sağlar
- UUIDv7'nin yalnızca timestamp alanını XOR ile maskeleyerek zaman bilgisinin açığa çıkmasını önler, diğer rastgele alanları ise değiştirmez
- Anahtarlı SipHash-2-4 ile maskeleme yaparak 128 bit anahtarla bilgiyi güvenli biçimde koruyabilir
- encode/decode süreci deterministik ve tamamen tersinirdir (orijinal değer eksiksiz geri elde edilebilir)
- Veritabanı saklama için (v7) ve dışa gösterim için (v4) UUID'ler arasında hızlı eşleme sağlar
- Test kodları ve benchmark araçları gibi zengin örnekler sunar
Kullanım amacı ve faydaları
- VT içinde indeks locality ve sayfalama verimliliğini en üst düzeye çıkaran sıralanabilir UUIDv7 kullanılabilir
- Dışarıya yalnızca UUIDv4 gibi görünen bir desen sunularak timestamp sızıntısı ve izleme riski engellenir
- SipHash kullanıldığı için anahtar geri elde edilemez, gizli anahtar güvenliği korunur
- RFC uyumlu sürüm/varyant bit işleme
- Çalışma hızı yüksek olduğundan gerçek zamanlı işleme ve yüksek hacimli üretim ortamlarında da verimlidir
Temel yapı ve iç çalışma prensibi
UUIDv7 Layout
- ts_ms_be: 48 bit big-endian timestamp
- ver: 6. baytın high nibble değeri (0x7=VT, 0x4=dış)
- rand_a: 12 bit rastgele değer
- var: RFC variant (0b10)
- rand_b: 62 bit rastgele değer
Maskeleme ve eşleme mantığı (Façade mapping)
- Kodlama: ts48 XOR mask48(R), version=4 olarak ayarlanır
- Çözme: encTS XOR mask48(R), version=7 olarak ayarlanır
- Rastgele alanlarda değişiklik yoktur
- SipHash girdisi olarak 10 baytlık rastgele alan kullanılır
- XOR maskeleme, anahtar bilindiğinde anında tersine çevrilebilir
Güvenlik modeli
- Hedef: Anahtar, girdileri seçmeli olarak verse bile açığa çıkmamalıdır
- Uygulama: SipHash-2-4 adlı anahtarlı sözde rastgele fonksiyon (PRF) kullanılır
- 128 bit anahtar kullanılır; anahtar türetimi için HKDF vb. önerilir
- Anahtar rotasyonunda bunu UUID içine yazmak yerine, ayrı küçük bir anahtar kimliği tutmak önerilir
Public API (C)
- uuidv47_encode_v4facade : v7→v4 dönüşümü
- uuidv47_decode_v4facade : v4→v7 geri yükleme
- Sürüm ayarlama, parse etme ve formatlama ile ilgili başka fonksiyonlar da sunulur
Performans ve benchmark
- SipHash maskeleme (10B) işleminde 14ns/op altı, encode+decode tam round trip ise 33ns/op düzeyindedir (Apple M1 bazında)
- Yüksek hacimli UUID üretimi ve eşlemesinde de hızlı işlem garantisi sunar
- En iyi performans
-O3 -march=native seçenekleriyle elde edilir
Entegrasyon ve genişletme
- API sınırında encode/decode yapılması önerilir
- PostgreSQL entegrasyonu için C uzantısı yazılabilir
- Sharding sırasında v4 façade, xxh3 veya SipHash ile hashlenebilir
Diğer
- Başka dil portları: Go (
n2p5/uuid47) vb. mevcuttur
- Önerilen hash: xxHash bir PRF olmadığı için bilgi sızıntısı riski taşır; SipHash önerilir
Lisans
- MIT lisansı (Stateless Limited, 2025)
1 yorum
Hacker News görüşü
Merhaba, ben uuidv47’nin yazarıyım. Temel fikir, içeride veritabanı indeksleme ve sıralanabilirlik için UUIDv7 kullanırken, dışarıya istemciye zamanlama örüntülerini sızdırmamak için UUIDv4 gibi görünen değerler vermek
Çalışma şekli, 48 bit zaman damgasını UUID’nin rastgele alanından türetilen SipHash-2-4 akışıyla XOR maskelemek
Rastgele bitler olduğu gibi korunuyor, sürüm içeride 7, dışarıda 4 olarak değişiyor ve RFC varyant değeri de korunuyor
Eşleme injective: yapı
(ts, rand) → (encTS, rand)Çözme işlemi
encTS ⊕ maskolduğu için kusursuz round-trip dönüşüm mümkünGüvenlik açısından SipHash bir PRF olduğu için, dışarıdan paketlenmiş değeri görmek anahtarı açığa çıkarmıyor
Anahtar yanlışsa zaman damgası da tamamen farklı çıkıyor
Anahtar kimliğini dışarıdan yöneterek anahtar rotasyonu da desteklenebilir
Performans olarak 10 baytta bir SipHash, birkaç 48 bit load/store kadar; yani nanosaniye düzeyinde ek yük, C11 header-only, dış bağımlılık yok ve allocation gerekmiyor
Testlerde SipHash referans vektörleri, round-trip encode/decode ve sürüm/varyant değişmezliği kontrol edildi
Geri bildirimi merak ediyorum
Bu fikri beğendim
UUID’ler çoğu zaman istemci tarafında üretiliyor; bu yöntemde bunun mümkün olmadığı anlaşılıyor
İstemcinin ürettiği UUID’yi alıp maskeli sürümü geri verseniz bile, ts farklı ama rand aynı olan iki UUID birisi tarafından verilebilir ve bu bir zafiyet doğurmaz mı?
Sonuç olarak bu yaklaşım yalnızca UUIDv7’yi doğrudan kendiniz ürettiğiniz durumlar için mi uygun, onu merak ediyorum
İki görüşüm var
Bu zahmete değecek kadar değer üretip üretmediğinden emin değilim
En büyük kaygım rastgele bitlerin entropi kalitesi
UUIDv7, öngörülebilirlikten çok çarpışma önlemeye odaklanıyor
Bu yüzden RFC, rastgele olmamayı zorunlu kılmak yerine öneri seviyesinde bırakıyor; zayıf PRNG ya da sayaç kullanımı, hatta rastgele bitlerin yerine ek saat verisi koyan uygulamalar bile var (bkz: RFC9562 s6.2 & s6.9)
Dolayısıyla v7’nin rand_a ve rand_b alanlarını doğrudan PRF için seed olarak kullanmak, veri güven sınırının dışından geliyorsa düşünüldüğünden daha riskli olabilir
PostgreSQL 18’in yeni
uuidv7()işlevi bile yüksek çözünürlüklü zaman damgasıyla rand_a alanını tamamen dolduruyor; bu da RFC açısından sorun sayılmıyorToplu import sırasında üretilen UUID’lere bakıldığında, bu v7-to-v4 yöntemiyle de gruplama yapılabildiği için bilgi sızabilir
Motor parçası telemetrisi gibi şeylerde sorun olmayabilir ama insanlarla doğrudan bağlantılı tanımlayıcı verilerde dikkatli olmak gerekir
Sonuç olarak, güvenilir entropiyi kendiniz garanti etmediğiniz sürece bu şema da zamanlama, seri ya da korelasyon bilgisi sızdırabilir; bu yüzden v7 uygulamasının kaynağını mutlaka doğrudan incelemek gerekir
Bunun iyi bir fikir olduğunu düşünmüyorum
PostgreSQL 18’de isteğe bağlı
shiftparametresi zaman damgasını verilen aralık kadar kaydırıyorhttps://www.postgresql.org/docs/18/functions-uuid.html
Birkaç yıl önce kendi şemamı tasarlayıp DB’de sıralı artan sayısal ID, dışarıda ise 4–20 karakter uzunluğunda kısa rastgele string kullandığım bir yöntem uygulamıştım
Bunun için Speck şifre ailesinin özelleştirilmiş bir örneğini kullanmıştım; sağlam ve oldukça makul olduğunu düşünüyorum
Tamamlamıştım ama bunu kullanacağım projeyi ertelediğim için yayımlamadım
Bu yıl ya da gelecek yıl ilgili materyali resmen yayımlamayı planlıyorum
Uygulama biçimi ile artı ve eksilerini iyi özetleyen notlarım da var; ilgileniyorsanız bakabilirsiniz
https://temp.chrismorgan.info/2025-09-17-tesid/
Ben de daha önce
bigserialPKID’leri Speck ile obfuscate etmeyi denemiştim ama çapraz platform uygulama desteği zayıftı, özellikle de pgcrypto tarafındaBu yüzden
base58(AES_K1(id{8} || HMAC_K2(id{8})[0..7]))seçtimSonuçlar genelde yaklaşık 22 karakterle daha uzun oluyor ama neredeyse her ortamda uygulanabiliyor ve performansı da fazlasıyla yeterli
İyi fikir
Benzer bir kavram olarak sqids’e (eski adıyla: hashids) de bakılabilir
https://sqids.org/
Benzer bir şeyi zamanında ben de yaşadım; herkese açık uuid ile API’ye hiç çıkmayan bigint PK için iki ayrı sütun tutuyorduk (uuidv7 çıkmadan çok önceydi)
uuid açısından biraz daha az kullanışlıydı ama PK’leri düzgün çıkardığınızda farklı DB dump’larını kolayca birleştirebilmek avantajdı
Hash tabanlı sorgulama olsa bile sonunda yine iki sütun gerekecek gibi geliyor ama hash’in çalışma mantığını yanlış anlamış da olabilirim
İstekteki
uuidv4değeri veritabanındakiuuidv7değerine dönüştürülebilirFikir ilginç ama keşke veritabanı bunu doğrudan desteklese
Yani UUIDv7 ile “UUIDv4” arasında karşılıklı dönüşüm yapılabilse ve sorgularda da iki format açıkça ayırt edilerek kullanılabilse
Gerçekten harika bir proje
dchest’in siphash kütüphanesini kullanarak bir Go uygulaması yaptım
https://github.com/n2p5/uuid47
Referans: https://github.com/dchest/siphash
Proje ilginç görünüyor; UUID v7’de zaman kısmının açığa çıkma riskini gerçek bir örnekle gösterebilir misiniz diye merak ediyorum
Kullanıcının davranış örüntüleri ya da sıraları ortaya çıkarsa rahatsız edici durumlar doğabilir
Tek tek mesajlar ya da gerçek zamanlı işlemler için önemli olmayabilir ama kullanıcı hesabı oluşturma ya da uzun ömürlü verilerde, biri bunu kimlik çıkarımı için kötüye kullanabilir
Eskiden bir CTF’de UUID’nin bir kısmını AES anahtarı olarak brute force etmiştim
Anahtar zaman kaynağından kısmen türetildiği için, anahtarın üretildiği andaki sistem saatini öğrenince saldırı mümkün oluyordu
Bir diğer basit örnek de bir dosya paylaşım servisinde yalnızca
websitesi.com/GUIDyapısının paylaşılması ve dosya yükleme zamanının ayrıca açıklanmaması durumuUUIDv7 kullanılıyorsa, yalnızca bundan dosya yükleme zamanını tahmin etmek mümkün olabilir
Bu mutlaka büyük bir güvenlik tehdidi olmayabilir ama yine de istenmeyen bir bilgi sızıntısıdır
Örneğin tıbbi veri saklayan bir sistemi düşünün
Analiz için MRI çekiminden hemen sonra sonuç yüklense ve kişisel bilgiler kaldırılmış olsa bile
UUIDv7 zaman damgası üzerinden dış korelasyon analizi yapılarak “bu tarihte MRI çektiren tek kişi buydu, demek bu onun MRI’ı” denebilir
UUIDv7’nin en can sıkıcı yanı, listelerde insanların bunu gözle karşılaştırmasının (
diff) çok zor olmasıpsql’de rastgele bitleri öne alan ama gerçek sıralamayı zaman damgasına göre koruyan bir görselleştirme katmanı olsa UX inanılmaz iyileşirdi
Ben sadece UUID’nin son kısmına bakmayı alışkanlık haline getirdim
Kendiniz bir fonksiyon yazıp sorguda kullanabilirsiniz
Örneğin hex gösteriminden sonra string’i ters çevirmek ya da reversed base64 kullanmak daha kısa ve ayırt etmesi daha kolay olabilir
Bu yaklaşım bana oldukça iyi görünüyor
Ama zaman damgasının açığa çıkmasına fazla panik yapılmasını ve sıralı ID’lerin görünmesinin doğrudan saldırı yüzeyi ya da iş bilgisi sızıntısı sayılmasını, gerçek bir güvenlik sorunu olmaktan çok gereksiz endişe gibi görüyorum
Belirli aralıklarla int değerine büyük bir rastgele sayı ekleseniz, monoton artış özelliği korunurken dış gözlemcinin örüntüyü kavraması da zorlaşır
Sonuçta önemli bilgi sızıyormuş gibi davranıp konuyu biraz fazla büyütme eğilimi de var bence
Sistemin kendi sızdırdığı bilgi tek başına çok anlamlı olmayabilir ama toplu halde ya da zaman serisi şeklinde gözlemlenirse ek veri çıkarımı yapılabilir
Örneğin David Kriesel’in SpiegelMining konuşmasında olduğu gibi, sadece gazete yazılarının tarihi ve yazarı toplanarak bile kimin ne zaman tatile çıktığına dair örüntüler çıkarılabiliyor
Farklı yazar verileri karşılaştırıldığında şirket içi ilişkiler gibi şeyler bile ortaya dökülebilir
Neden oturum başına farklı bir şifreleme anahtarı kullanıp dışarıya sadece şifrelenmiş id göstermiyorsunuz diye merak ediyorum
Böyle olursa DB’de sadece basit sıralı id kullanmak yeterli olmaz mı?
Anahtarı düzenli olarak değiştirirseniz anahtar yönetimi inanılmaz karmaşık hale gelir; o anda doğru anahtarın nasıl bulunacağı da ayrı bir sorun olur
Neden sürüm 4 yerine sürüm 8 kullanılmadığını merak ediyorum
v4 rastgele bitler anlamına geliyor ama gerçekte o kadar da rastgele değil
v8’de bitlerin anlamı üzerinde böyle bir kısıtlama yok
Bu yaklaşımın amacı zaten dışarıdan rastgele görünmek olduğu için, belki de v8 daha fazla dikkat çekerdi diye düşünüyorum