Randevu Bağlantısı Güvenliği: Sıralı Kimlik Neden Tehlikeli?

Hastaya gönderilen randevu bağlantısındaki kimlik tahmin edilebiliyorsa başka hastaların bilgileri okunabilir. UUID neden yetmez, doğru çözüm nedir?

Hastaya bir randevu bağlantısı gönderdiğinizde, o bağlantı bir kimlik doğrulama aracına dönüşür: bağlantıyı bilen kişi randevuyu görebilir. Bu tasarım tercihinin kendisi makuldur — hastadan şifre istemek açılma oranını yerle bir eder. Ancak bu tercih, adresteki kimliğin tahmin edilemez olmasını zorunlu kılar.

Pratikte en sık yapılan hata budur ve genellikle fark edilmez.

Birinci hata: veritabanı kimliği

En yaygın biçim şudur:

https://randevu.ornek.com/detay?id=48213

Bağlantıyı alan herhangi bir kişi 48212, 48214 deneyerek başka hastaların adına, telefonuna, hekimine ve randevu saatine ulaşır. Bu bir saldırı bile değildir — adres çubuğunda bir rakam değiştirmektir. OWASP bunu Insecure Direct Object Reference olarak adlandırır ve web güvenlik listelerinde onlarca yıldır ilk sıralardadır.

İkinci hata: UUID'nin yeterli sanılması

"Sıralı kimlik kötüyse UUID kullanalım" doğru içgüdüdür ama eksik uygulanır. Şu adres güvenli görünür:

https://randevu.ornek.com/?GUID=a8faa905-477a-11f0-89df-52e18006e243

Sorun şudur: MySQL'in UUID() fonksiyonu sürüm 1 UUID üretir. Sürüm 1 rastgele değildir; zaman damgası ve sunucunun MAC adresinden türetilir. Yukarıdaki değeri parçalarına ayıralım:

ParçaDeğerNe içerir
1–3a8faa905-477a-11f060 bitlik zaman damgası (100 ns çözünürlük)
489dfSaat dizisi — sunucu yeniden başlarsa değişir
552e18006e243Sunucunun MAC adresi — bütün kayıtlarda aynı

Yani elinde tek bir bağlantı olan kişi son parçayı zaten bilir. Aynı saniyede oluşturulmuş randevuların ilk parçaları da birbirine çok yakındır. Arama uzayı, düşünüldüğü gibi 2122 değil; pratikte kaba kuvvetle taranabilecek kadar dardır.

Sürüm 1 UUID bir tanımlayıcıdır, bir sırdır değildir. Veritabanı içinde birincil anahtar olarak kusursuzdur; adres çubuğunda güvenlik sağlamaz.

Doğru çözüm: ayrı bir belirteç

Randevunun sistem içindeki kimliği ile hastanın adresindeki değer farklı iki şey olmalıdır. Bağlantı için kriptografik olarak güvenli bir rastgele üreteçten ayrı bir belirteç üretin ve eşleşmeyi veritabanında tutun:

// 16 bayt = 128 bit gerçek rastgelelik, 32 karakter onaltılık
$token = bin2hex(random_bytes(16));

// randevu_baglantilari: token -> (kurum, sube, randevu_kimligi)

Sonuç adres şuna benzer:

https://ihbys.com/r/9f3c1ad84be207e5c6a1d0f4b83e77a2

Bu değerin tahmin edilmesi pratikte imkânsızdır ve komşu randevular hakkında hiçbir bilgi sızdırmaz. random_bytes() (PHP), secrets (Python) ve crypto.randomBytes() (Node) işletim sisteminin kriptografik üretecini kullanır; rand(), mt_rand() veya uniqid() bu iş için uygun değildir.

Bağlantı yeterli mi? Katmanlar

Tahmin edilemez bir bağlantı, tahmin yoluyla erişimi kapatır. Bağlantının başkasının eline geçmesi ihtimalini kapatmaz — telefonun ödünç verilmesi, ekran görüntüsünün paylaşılması, yanlış numaraya gönderim. Bunun için ek katmanlar gerekir:

Süre sınırı

Bağlantı sonsuza kadar yaşamamalı. Randevu tarihinden 30 gün sonra kapanan bir bağlantı, eski bir mesaj kutusunda unutulmuş bağlantının yıllar sonra kullanılmasını engeller.

İsteğe bağlı ikinci soru

Sayfa açılmadan önce doğum yılı ya da T.C. kimliğin son dört hanesi sorulabilir. Bu, telefonu ödünç alan kişiyi durdurur. Bedeli açılma oranındaki düşüştür; kurumun risk iştahına göre açılıp kapatılabilmelidir.

Hatalı deneme sayısı mutlaka sınırlanmalıdır — dört haneli bir kodun 10.000 olasılığı vardır ve sınırsız deneme hakkı verilirse birkaç dakikada tükenir.

Maskeli gösterim

Ekranda T.C. kimlik 123******01, telefon 0532 *** ** 67 biçiminde gösterilmelidir. Hasta kendi kaydını teyit edebilir; omzunun üzerinden bakan kişi numarayı öğrenemez.

Sızıntıyı önleyen başlıklar

Randevu sayfası şunları göndermelidir:

Cache-Control: no-store, no-cache, must-revalidate, private
Referrer-Policy: no-referrer
X-Robots-Tag / meta robots: noindex, nofollow, noarchive

no-referrer özellikle önemlidir: hasta sayfadaki bir yol tarifi bağlantısına tıkladığında, referrer başlığı olmadan hedef site randevu adresini öğrenemez. Ayrıca robots.txt ile bu yolu kapatın — arama motorunda dizine düşmüş bir randevu sayfası, bir veri ihlalidir.

Yazma yetkisi vermeyin

Son ve belki en önemli nokta: bağlantıyı açan kişinin randevuyu silebilmesi tasarım hatasıdır. Bugün birçok sistemde "iptal et" düğmesi doğrudan bir DELETE sorgusu çalıştırır.

Bunun yerine hasta bir talep oluşturmalıdır. Talep klinik personeline düşer, randevu onaylanana kadar yerinde durur. Bu yaklaşım üç sorunu birden çözer: yanlışlıkla yapılan iptalleri, kötü niyetli iptalleri ve kliniğin habersiz kalmasını.

Kontrol listesi

  • Adresteki değer random_bytes() ile üretiliyor mu?
  • Veritabanı kimliği ya da UUID adrese yazılıyor mu? (yazılmamalı)
  • Bağlantının bir sona erme tarihi var mı?
  • Hatalı doğrulama denemeleri sınırlanıyor mu?
  • T.C. ve telefon maskeli mi?
  • noindex, no-store, no-referrer gönderiliyor mu?
  • robots.txt bu yolu kapatıyor mu?
  • Hasta randevuyu silebiliyor mu? (silememeli)

Bu sekiz maddenin tamamı, mevcut bir sistemde birkaç günlük bir çalışmayla düzeltilebilir. Düzeltilmediğinde bedeli, KVKK kapsamında bildirilmesi gereken bir veri ihlalidir.

güvenlik kvkk randevu linki uuid

ihbys, bu yazıda anlatılanları hazır bir ekran olarak sunar: randevu bilgisi, konum, takvime ekleme, iptal ve değişiklik talebi.

Demo talep edin